如何访问 Spring 中的缓存值
How to access cache values in Spring
场景:我需要在另一种方法中访问作为一种方法的一部分创建的缓存的值。我该怎么做?
public class Invoice {
private String invoiced;
private BigDecimal amount;
//Getters and Setters
}
方法 1:当客户想要从 UI
获取发票列表时调用
@Cacheable(value="invoices")
public List<Invoice> getAllInvoices(String customerId){
...
//Get all invoices from Database and return
...
return invoices
}
方法 2:当客户点击 UI
上的下载时调用
public File downloadInvoice(String invoiceId) {
//TODO:
//Validate if invoiceId is present in the cache. This is a validation step
//How can I access the cache "invoices" here.
...
//if InvoiceId is present in cache then download from db else throw Exception
return file;
}
注意:我没有使用任何缓存库
当您将方法注释为 @Cacheable
时,对该方法的第一次调用将在缓存中存储该方法的结果。对该方法的所有后续调用都将 return 缓存的结果,除非缓存被逐出。
getAllInvoices
的正常调用将 return 缓存的结果(如果先前已缓存结果)。
如需进一步阅读,我会推荐这本优秀的 article
正如 Spring 的缓存抽象 上的 documentation (and here) 所解释的那样,您必须启用缓存(即使用 @EnableCaching
注释注释配置,或使用 <cache:annotation-driven>
元素和 XML 配置) 和 声明类型 CacheManager
的 bean 以插入您选择的缓存提供程序(例如 Redis).
当然,当您使用 Spring Boot 时,这两个问题都是“auto-configured”你,前提是你已经在你的 Spring Boot 应用程序的类路径。
有关 Spring Boot 支持的缓存提供程序的完整列表(即“自动配置”),请参阅here.
这个(CacheManager
bean 的here) is Spring Boot's caching auto-configuration for Redis, when Redis is on the application's classpath. Notice the declaration,必须准确命名为“cacheManager”。此设置对所有缓存提供程序都是相同的。
现在您知道 CacheManager
类型的“cacheManager”bean 存在于 Spring ApplicationContext
中,您可以使用CacheManager
到 access the individual caches, which are represented by the Cache
(Adapter) interface.
然后你可以...
@Service
class InvoiceService {
private CacheManager cacheManager;
public InvoiceService(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
public File downloadInvoice(String invoiceId) {
// Note: name of cache here (i.e. "invoices") must match the name
// in the `@Cacheable` annotation on getAllInvoices(..).
Cache invoices = this.cacheManager.getCache("invoices");
// Now use the "invoices" `Cache` to find a reference to a `Invoice` with "invoiceId".
// Read/load from database if `Invoice` is "cached"...
// Do what must be done, etc ...
}
}
但是,你有问题。
您的 @Cacheable
、getAllInvoices(..)
方法将“customerId”(键)缓存(即映射)到“发票”缓存中的 List<Invoice>
对象(值)。
您可能认为 @Cacheable
、getAllInvoices(..)
服务方法返回的 List
或 Invoices
由“invoiceId”单独缓存。但是,我向你保证,事实并非如此!
其实是...
customerId -> List<Invoice>
也就是说,由“发票”缓存中的“customerId”映射的 List
个 Invoice
个对象。
有一些方法可以改变默认行为(例如,如果需要,可以通过“invoiceId”在“invoices”缓存中缓存 List
中的单个 Invoice
对象,我在其他文章中对此进行了解释SO 关于缓存集合的帖子),但那是不是您的应用程序逻辑当前的设置和运行方式!
因此,您需要将“invoiceId”转换为“customerId”,以便通过“customerId”访问“invoices”Cache
中的 List
个 Invoice
个对象,然后迭代 List
以找到(可能的)由“invoiceId”缓存的 Invoice
。
或者,您可以更改缓存逻辑(推荐)。
终于...
没有缓存提供程序在后台是相同的。可能有一种方法可以独立访问各个缓存,具体取决于提供者但是,一般来说,您应该记住 Spring 的 Cache
表示在 Spring 的 缓存注释(例如 @Cacheable
)或等效的 JSR-107、JCache API 注释 equivalents,不是 Spring 容器中的实际“豆”(不同于 CacheManager
)。
在 Redis 中,缓存是 Redis HASHES(我相信)。
在GemFire/Geode(我最熟悉)中,缓存(即Cache
)是一个GemFire/GeodeRegion
,做 正好是 Spring bean 在 ApplicationContext
.
此外,一些缓存提供程序还使用适当的模板(例如 GemfireTemplate
,按区域)包装支持 Cache
的底层数据结构(例如 GemFire/Geode 区域)。
我不确定 (Sprig Data) Redis 是否为支持 Cache
的每个 HASH 创建了一个 RedisTemplate
。然而,GemFire/Geode就是这种情况。所以你也可以做这样的事情......
@Service
class InvoiceService {
@Resource(name = "invoices")
private Region<String, Invoice> invoices;
// ...
}
或者,或者(推荐)...
@Service
class InvoiceService {
@Autowired
@Qualifier("invoices")
GemfireTemplate invoicesTemplate;
// ...
}
同样,这因缓存提供商而异,并且非常特定于提供商。
Cache
适配器接口是引用后备缓存实现的通用方式,如果您希望在环境之间切换缓存提供程序或出于其他原因,它很有用。
同样,要访问单个缓存(例如“发票”),您需要注入 CacheManager
,因为并非所有缓存提供程序都为单个 Cache
个实例创建 bean。
请记住,我认为您将不得不稍微更改缓存设计。
希望这对您有所帮助。
场景:我需要在另一种方法中访问作为一种方法的一部分创建的缓存的值。我该怎么做?
public class Invoice {
private String invoiced;
private BigDecimal amount;
//Getters and Setters
}
方法 1:当客户想要从 UI
获取发票列表时调用@Cacheable(value="invoices")
public List<Invoice> getAllInvoices(String customerId){
...
//Get all invoices from Database and return
...
return invoices
}
方法 2:当客户点击 UI
上的下载时调用public File downloadInvoice(String invoiceId) {
//TODO:
//Validate if invoiceId is present in the cache. This is a validation step
//How can I access the cache "invoices" here.
...
//if InvoiceId is present in cache then download from db else throw Exception
return file;
}
注意:我没有使用任何缓存库
当您将方法注释为 @Cacheable
时,对该方法的第一次调用将在缓存中存储该方法的结果。对该方法的所有后续调用都将 return 缓存的结果,除非缓存被逐出。
getAllInvoices
的正常调用将 return 缓存的结果(如果先前已缓存结果)。
如需进一步阅读,我会推荐这本优秀的 article
正如 Spring 的缓存抽象 上的 documentation (and here) 所解释的那样,您必须启用缓存(即使用 @EnableCaching
注释注释配置,或使用 <cache:annotation-driven>
元素和 XML 配置) 和 声明类型 CacheManager
的 bean 以插入您选择的缓存提供程序(例如 Redis).
当然,当您使用 Spring Boot 时,这两个问题都是“auto-configured”你,前提是你已经在你的 Spring Boot 应用程序的类路径。
有关 Spring Boot 支持的缓存提供程序的完整列表(即“自动配置”),请参阅here.
这个(CacheManager
bean 的here) is Spring Boot's caching auto-configuration for Redis, when Redis is on the application's classpath. Notice the declaration,必须准确命名为“cacheManager”。此设置对所有缓存提供程序都是相同的。
现在您知道 CacheManager
类型的“cacheManager”bean 存在于 Spring ApplicationContext
中,您可以使用CacheManager
到 access the individual caches, which are represented by the Cache
(Adapter) interface.
然后你可以...
@Service
class InvoiceService {
private CacheManager cacheManager;
public InvoiceService(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
public File downloadInvoice(String invoiceId) {
// Note: name of cache here (i.e. "invoices") must match the name
// in the `@Cacheable` annotation on getAllInvoices(..).
Cache invoices = this.cacheManager.getCache("invoices");
// Now use the "invoices" `Cache` to find a reference to a `Invoice` with "invoiceId".
// Read/load from database if `Invoice` is "cached"...
// Do what must be done, etc ...
}
}
但是,你有问题。
您的 @Cacheable
、getAllInvoices(..)
方法将“customerId”(键)缓存(即映射)到“发票”缓存中的 List<Invoice>
对象(值)。
您可能认为 @Cacheable
、getAllInvoices(..)
服务方法返回的 List
或 Invoices
由“invoiceId”单独缓存。但是,我向你保证,事实并非如此!
其实是...
customerId -> List<Invoice>
也就是说,由“发票”缓存中的“customerId”映射的 List
个 Invoice
个对象。
有一些方法可以改变默认行为(例如,如果需要,可以通过“invoiceId”在“invoices”缓存中缓存 List
中的单个 Invoice
对象,我在其他文章中对此进行了解释SO 关于缓存集合的帖子),但那是不是您的应用程序逻辑当前的设置和运行方式!
因此,您需要将“invoiceId”转换为“customerId”,以便通过“customerId”访问“invoices”Cache
中的 List
个 Invoice
个对象,然后迭代 List
以找到(可能的)由“invoiceId”缓存的 Invoice
。
或者,您可以更改缓存逻辑(推荐)。
终于...
没有缓存提供程序在后台是相同的。可能有一种方法可以独立访问各个缓存,具体取决于提供者但是,一般来说,您应该记住 Spring 的 Cache
表示在 Spring 的 缓存注释(例如 @Cacheable
)或等效的 JSR-107、JCache API 注释 equivalents,不是 Spring 容器中的实际“豆”(不同于 CacheManager
)。
在 Redis 中,缓存是 Redis HASHES(我相信)。
在GemFire/Geode(我最熟悉)中,缓存(即Cache
)是一个GemFire/GeodeRegion
,做 正好是 Spring bean 在 ApplicationContext
.
此外,一些缓存提供程序还使用适当的模板(例如 GemfireTemplate
,按区域)包装支持 Cache
的底层数据结构(例如 GemFire/Geode 区域)。
我不确定 (Sprig Data) Redis 是否为支持 Cache
的每个 HASH 创建了一个 RedisTemplate
。然而,GemFire/Geode就是这种情况。所以你也可以做这样的事情......
@Service
class InvoiceService {
@Resource(name = "invoices")
private Region<String, Invoice> invoices;
// ...
}
或者,或者(推荐)...
@Service
class InvoiceService {
@Autowired
@Qualifier("invoices")
GemfireTemplate invoicesTemplate;
// ...
}
同样,这因缓存提供商而异,并且非常特定于提供商。
Cache
适配器接口是引用后备缓存实现的通用方式,如果您希望在环境之间切换缓存提供程序或出于其他原因,它很有用。
同样,要访问单个缓存(例如“发票”),您需要注入 CacheManager
,因为并非所有缓存提供程序都为单个 Cache
个实例创建 bean。
请记住,我认为您将不得不稍微更改缓存设计。
希望这对您有所帮助。