JCache (JSR-107) Annotations

自 4.1 版本以来,Spring 的缓存抽象完全支持 JCache 标准 (JSR-107) 注解:@CacheResult@CachePut@CacheRemove@CacheRemoveAll,以及 @CacheDefaults@CacheKey@CacheValue 伙伴。即使不将缓存存储迁移到 JSR-107,您也可以使用这些注释。内部实现使用 Spring 的缓存抽象,并提供与规范兼容的默认 CacheResolverKeyGenerator 实现。换句话说,如果您已经在使用 Spring 的缓存抽象,则可以在不更改缓存存储(或配置)的情况下切换到这些标准注释。

Feature Summary

对于熟悉 Spring 的缓存注释的人来说,下表描述了 Spring 注释与其 JSR-107 对应项之间的主要差异:

Table 1. Spring vs. JSR-107 caching annotations
Spring JSR-107 Remark

@Cacheable

@CacheResult

非常相似。@CacheResult 可缓存特定的异常,并迫使方法执行,而无需考虑缓存的内容。

@CachePut

@CachePut

当 Spring 使用方法调用的结果更新缓存时,JCache 需要将它作为一个带 @CacheValue 注释的参数传递。由于这种差异,JCache 可以在实际方法调用之前或之后更新缓存。

@CacheEvict

@CacheRemove

非常相似。@CacheRemove 在方法调用导致异常时支持条件驱逐。

@CacheEvict(allEntries=true)

@CacheRemoveAll

See @CacheRemove.

@CacheConfig

@CacheDefaults

按照类似的方式,可用于配置相同概念。

JCache 有 javax.cache.annotation.CacheResolver 的概念,它与 Spring 的 CacheResolver 接口相同,只是 JCache 只支持一个缓存。默认情况下,一个简单的实现基于注释中声明的名称检索要使用的缓存。需要注意的是,如果注释中未指定缓存名称,则会自动生成一个默认名称。有关详细信息,请参阅 @CacheResult#cacheName() 的 Java 文档。

CacheResolver 实例由 CacheResolverFactory 检索。可以为每个缓存操作自定义工厂,如下例所示:

@CacheResult(cacheNames="books", cacheResolverFactory=MyCacheResolverFactory.class) 1
public Book findBook(ISBN isbn)
1 自定义此操作的工厂。

对于所有引用的类,Spring 尝试查找给定类型的 Bean。如果存在多个匹配项,则会创建一个新实例,并且可以使用常规 Bean 生命周期回调,例如依赖注入。

键由 javax.cache.annotation.CacheKeyGenerator 生成,其用途与 Spring 的 KeyGenerator 相同。默认情况下,所有方法参数都将被考虑在内,除非使用 @CacheKey 标记至少一个参数。这与 Spring 的 custom key generation declaration 类似。例如,以下操作是相同的,一个使用 Spring 抽象,另一个使用 JCache:

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@CacheResult(cacheName="books")
public Book findBook(@CacheKey ISBN isbn, boolean checkWarehouse, boolean includeUsed)

您还可以在操作中指定 CacheKeyResolver,类似于指定 CacheResolverFactory

JCache 可以管理注释方法抛出的异常。这可以阻止更新缓存,但也可以将异常缓存为失败指标,而不是再次调用该方法。假设在 ISBN 结构无效时抛出 InvalidIsbnNotFoundException。这是一个永久性故障(永远无法使用这样的参数检索到书)。以下代码缓存该异常,以便使用相同的无效 ISBN 进行的进一步调用直接抛出缓存异常,而不是再次调用该方法:

@CacheResult(cacheName="books", exceptionCacheName="failures"
			cachedExceptions = InvalidIsbnNotFoundException.class)
public Book findBook(ISBN isbn)

Enabling JSR-107 Support

您无需执行任何特定操作即可启用 JSR-107 支持以及 Spring 的声明性注释支持。如果 JSR-107 API 和 spring-context-support 模块同时存在于类路径中,则 @EnableCachingcache:annotation-driven XML 元素都会自动启用 JSR-107 支持。

根据使用场景,基本由你决定。你甚至可以通过在一些服务上使用 JSR-107 API,并在其他服务上使用 Spring 自己的注释来混合和匹配服务。但是,如果这些服务影响同一缓存,则应使用一致且相同的密钥生成实现。