如何在Spring中访问缓存值

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)

注意:我没有使用任何缓存库

Joh*_*lum 5

正如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) 接口表示的各个缓存。 ApplicationContextCacheManagerCache

那么你可以...

@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@CacheablegetAllInvoices(..)

它实际上是...

customerId -> List<Invoice>

即“invoices”缓存中“customerId”映射的对象List的一个​​。Invoice

有多种方法可以更改默认行为(例如,如果需要,可以在“发票”Invoice缓存中缓存“invoiceId”中的单个对象List,我在其他有关缓存集合的 SO 帖子中对此进行了解释),但这不是的应用程序逻辑的方式当前已设置并将运行!

因此,您需要将“invoiceId”转换为“customerId”,以通过“customerId”访问“invoices”中的对象List,然后迭代以查找“invoiceId”缓存的(可能的)对象。InvoiceCacheListInvoice

或者,您可以更改缓存逻辑(推荐)。

最后...

没有哪个缓存提供商的底层是相同的。可能有一种方法可以独立地访问各个缓存,具体取决于提供者。但是,一般来说,您应该记住Spring 的缓存注释(例如)或等效的 JSR-107、JCache API 注释中标识的各个缓存的Spring表示形式 等价物,不是Spring容器中实际的“bean”(与)不同。Cache@CacheableCacheManager

在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。

请记住,我认为您将不得不稍微更改一下缓存设计。

希望这可以帮助。