sum*_*nik 4 spring caching spring-mvc spring-boot spring-cache
场景:我需要在另一种方法中访问作为一种方法的一部分创建的缓存的值。我该怎么做?
public class Invoice {
private String invoiced;
private BigDecimal amount;
//Getters and Setters
}
Run Code Online (Sandbox Code Playgroud)
方法 1:当客户想要从 UI 获取发票列表时调用
@Cacheable(value="invoices")
public List<Invoice> getAllInvoices(String customerId){
...
//Get all invoices from Database and return
...
return invoices
}
Run Code Online (Sandbox Code Playgroud)
方法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;
}
Run Code Online (Sandbox Code Playgroud)
注意:我没有使用任何缓存库
正如Spring 缓存抽象的文档(以及这里)所解释的,您必须启用缓存(即使用带有注释配置的注释,或使用带有 XML 配置的元素)并声明一个类型的 bean来插入您选择的缓存提供程序(例如雷迪斯)。@EnableCaching
<cache:annotation-driven>
CacheManager
当然,当您使用Spring Boot时,只要您在Spring Boot应用程序的类路径上声明了适当的缓存提供程序(即实现Spring 的 Cache Abstraction 的缓存提供程序) ,这两个问题都会为您“自动配置” 。
有关Spring Boot支持(即“自动配置”)的缓存提供程序的完整列表,请参阅此处。
当 Redis 位于应用程序的类路径上时,这(此处)是Spring Boot 对 Redis 的缓存自动配置。请注意bean的声明CacheManager
,它必须准确命名为“cacheManager”。此设置对于所有缓存提供程序都是相同的。
现在您知道SpringCacheManager
中存在类型为“cacheManager”的 bean ,您可以使用 来访问由(Adapter) 接口表示的各个缓存。 ApplicationContext
CacheManager
Cache
那么你可以...
@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 ...
}
}
Run Code Online (Sandbox Code Playgroud)
但是,你有问题。
您的@Cacheable
,getAllInvoices(..)
方法将“customerId”(键)缓存(即映射)到List<Invoice>
“发票”缓存中的对象(值)。
您可能会认为从您的服务方法返回的List
结果是通过“invoiceId”单独缓存的。然而,我向你保证,事实并非如此!Invoices
@Cacheable
getAllInvoices(..)
它实际上是...
customerId -> List<Invoice>
即“invoices”缓存中“customerId”映射的对象List
的一个。Invoice
有多种方法可以更改默认行为(例如,如果需要,可以在“发票”Invoice
缓存中缓存“invoiceId”中的单个对象List
,我在其他有关缓存集合的 SO 帖子中对此进行了解释),但这不是您的应用程序逻辑的方式当前已设置并将运行!
因此,您需要将“invoiceId”转换为“customerId”,以通过“customerId”访问“invoices”中的对象List
,然后迭代以查找“invoiceId”缓存的(可能的)对象。Invoice
Cache
List
Invoice
或者,您可以更改缓存逻辑(推荐)。
最后...
没有哪个缓存提供商的底层是相同的。可能有一种方法可以独立地访问各个缓存,具体取决于提供者。但是,一般来说,您应该记住Spring 的缓存注释(例如)或等效的 JSR-107、JCache API 注释中标识的各个缓存的Spring表示形式 等价物,不是Spring容器中实际的“bean”(与)不同。Cache
@Cacheable
CacheManager
在Redis中,缓存是Redis HASHES(我相信)。
在 GemFire/Geode (我最熟悉的)中,缓存(即)是一个GemFireCache
/Geode Region
,它恰好是.ApplicationContext
此外,一些缓存提供程序还包装底层数据结构(例如 GemFire/Geode Region),Cache
以适当的模板(例如GemfireTemplate
,按区域)支持。
我不确定(Sprig Data)Redis是否RedisTemplate
为每个支持 a 的 HASH创建一个Cache
. 然而,GemFire/Geode 就是这种情况。所以你也可以做这样的事情......
@Service
class InvoiceService {
@Resource(name = "invoices")
private Region<String, Invoice> invoices;
// ...
}
Run Code Online (Sandbox Code Playgroud)
或者,或者(推荐)...
@Service
class InvoiceService {
@Autowired
@Qualifier("invoices")
GemfireTemplate invoicesTemplate;
// ...
}
Run Code Online (Sandbox Code Playgroud)
同样,这因缓存提供商而异,并且是特定于提供商的。
AdapterCache
接口是引用后备缓存实现的通用方法,如果您希望在环境之间切换缓存提供程序或出于其他原因,则该接口非常有用。
同样,要访问各个缓存(例如“发票”),您需要注入 Bean,CacheManager
因为并非所有缓存提供程序都会为各个Cache
实例创建 bean。
请记住,我认为您将不得不稍微更改一下缓存设计。
希望这可以帮助。