32.4 JCache (JSR-107) 注解

Spring Framework 4.1 以来,缓存抽象完全支持 JCache 标准:即 @CacheResult@CachePut@CacheRemove@CacheRemoveAll 还有 @CacheDefaults@CacheKey@CacheValue 。这些注解被大家正确的使用,体现了缓存在 JSR-107 的实现:缓存抽象的内部实现,并提供了符合规范的默认 CacheResolverKeyGenerator 的实现。换句话说,如果你已经使用了 Spring 缓存抽象,那可以平滑切换到这些标准注解,无需更改缓存存储(或者配置)。

32.4.1 特征总结

对于熟悉 Spring 缓存注解,下面描述了 Spring 注解和 JSR-107 对应的主要区别:

表 32.3. Spring vs JSR-107 缓存注解

Spring JSR-107 备注
@Cacheable @CacheResult 相似,@CacheResult 可缓存特定的异常,并强制执行该方法,不管缓存的结果。
@CachePut @CachePut Spring 调用方法之后获取结果去更新缓存,但 JCache 将其作为使用 @CacheValue 的参数传递。所以 JCache 允许实际方法调用之前或者之后更新缓存。
@CacheEvict @CacheRemove 相似, @CacheRemove 支持条件回收,以防止方法调用导致异常
@CacheEvict(allEntries=true) @CacheRemoveAll 查阅 @CacheRemove
@CacheConfig @CacheDefaults 允许类似的方式配置相同的属性

JCache 的 javax.cache.annotation.CacheResolver 概念和 Spring 的 CacheResolver 是一样的,除了 JCache 只支持单个缓存。默认下,根据注解声明的名称检索要使用的缓存。如果没有指定缓存的名称,则会自动生成默认值。更多信息可以查看 javadoc 的 @CacheResult#cacheName()

CacheResolverFactory 检索出 CacheResolver 实例。每个缓存操作都可以自定义工厂:

@CacheResult(cacheNames="books", cacheResolverFactory=MyCacheResolverFactory.class)
public Book findBook(ISBN isbn)
注意
对于所有引用的类,Spring 会找到具体指定类型的 Bean。如果存在多个匹配,会创建一个新实例,并可以正常的 Bean 生命周期回调(如依赖注入)

键由 javax.cache.annotation.CacheKeyGenerator 生成,和 Spring 的 KeyGenerator 能达到相同的目标。默认情况下,所有方法参数都会被考虑,除非至少有一个参数被注解为 @CacheKey。这和 Spring 的自定义键生成类似。例如,下面代码操作是相同的,一个使用 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 管理注解方法抛出的异常:可以防止缓存更新,但将异常作为故障的标志,而不是再次调用该方法。假设抛出 InvalidIsbnNotFoundException 异常,那么 ISBN 的结构是无效的。如果这是一个永久的失败,没有任何书会被查询出来。以下的缓存异常,以使具有无效的 ISBN 进一步直接抛出缓存的异常,而不是再次调用该方法。

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

32.4.2 启用 JSR-107 支持

不需要特殊处理,去支持 JSR-107 和 Spring 的声明式注解。JSR-17 API 和 spring-context-support 模块在类路径下,@EnableCachingcache:annotation-driven 两者会被启用。

根据你的具体案例选择需要的。你还可以使用 JSR-107 API 和其他使用 Spring 自己的注解来匹配服务。注意,如果这些服务影响相同的缓存,则使用一致的键生成实现。