对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

JCache (JSR-107) 注解

自 4.1 版本起,Spring 的缓存抽象完全支持 JCache 标准(JSR-107)注解:@CacheResult@CachePut@CacheRemove@CacheRemoveAll,以及配套的 @CacheDefaults@CacheKey@CacheValue。 即使您尚未将缓存存储迁移到 JSR-107,也可以使用这些注解。 其内部实现基于 Spring 的缓存抽象,并提供了符合规范的默认 CacheResolverKeyGenerator 实现。 换句话说,如果您已经在使用 Spring 的缓存抽象,则可以切换到这些标准注解,而无需更改您的缓存存储(或相应配置)。spring-doc.cadn.net.cn

功能摘要

对于熟悉 Spring 缓存注解的用户,下表描述了 Spring 注解与其对应的 JSR-107 注解之间的主要区别:spring-doc.cadn.net.cn

表1. Spring 与 JSR-107 缓存注解对比
Spring JSR-107 备注

@Cacheablespring-doc.cadn.net.cn

@CacheResultspring-doc.cadn.net.cn

非常相似。@CacheResult 可以缓存特定的异常,并强制执行方法,而不考虑缓存中的内容。spring-doc.cadn.net.cn

@CachePutspring-doc.cadn.net.cn

@CachePutspring-doc.cadn.net.cn

虽然 Spring 使用方法调用的结果来更新缓存,但 JCache 要求将该结果作为参数传递,并使用 @CacheValue 注解进行标注。 由于这一差异,JCache 允许在实际方法调用之前或之后更新缓存。spring-doc.cadn.net.cn

@CacheEvictspring-doc.cadn.net.cn

@CacheRemovespring-doc.cadn.net.cn

非常相似。@CacheRemove 在方法调用引发异常时支持条件性驱逐缓存。spring-doc.cadn.net.cn

@CacheEvict(allEntries=true)spring-doc.cadn.net.cn

@CacheRemoveAllspring-doc.cadn.net.cn

参见 @CacheRemovespring-doc.cadn.net.cn

@CacheConfigspring-doc.cadn.net.cn

@CacheDefaultsspring-doc.cadn.net.cn

让你以类似的方式配置相同的概念。spring-doc.cadn.net.cn

JCache 引入了 javax.cache.annotation.CacheResolver 的概念,它与 Spring 的 CacheResolver 接口类似,不同之处在于 JCache 仅支持单个缓存。默认情况下,一个简单的实现会根据注解中声明的名称来获取要使用的缓存。需要注意的是,如果注解中未指定缓存名称,则会自动生成一个默认名称。更多信息请参见 @CacheResult#cacheName() 的 javadoc。spring-doc.cadn.net.cn

CacheResolver 实例由 CacheResolverFactory 获取。可以为每个缓存操作自定义该工厂,如下例所示:spring-doc.cadn.net.cn

@CacheResult(cacheNames="books", cacheResolverFactory=MyCacheResolverFactory.class) (1)
public Book findBook(ISBN isbn)
1 为此操作自定义工厂。
对于所有被引用的类,Spring 会尝试查找具有给定类型的 bean。 如果存在多个匹配项,则会创建一个新实例,并可以使用常规的 bean 生命周期回调方法,例如依赖注入。

键由 javax.cache.annotation.CacheKeyGenerator 生成,其作用与 Spring 的 KeyGenerator 相同。默认情况下,所有方法参数都会被纳入考虑,除非至少有一个参数使用了 @CacheKey 注解。这与 Spring 的自定义键生成声明类似。例如,以下两个操作是等效的,一个使用 Spring 的抽象,另一个使用 JCache:spring-doc.cadn.net.cn

@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 的方式类似。spring-doc.cadn.net.cn

JCache 可以管理由带注解的方法抛出的异常。这不仅可以防止缓存被更新,还可以将异常本身缓存起来,作为失败的标识,从而避免再次调用该方法。假设当 ISBN 结构无效时,会抛出 InvalidIsbnNotFoundException 异常。这是一种永久性失败(使用这样的参数永远无法检索到任何图书)。以下配置会将该异常缓存起来,使得后续使用相同无效 ISBN 的调用直接抛出已缓存的异常,而不再重新调用该方法:spring-doc.cadn.net.cn

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

启用 JSR-107 支持

你无需执行任何特定操作即可在 Spring 的声明式注解支持旁边启用 JSR-107 支持。只要类路径中同时存在 JSR-107 API 和 @EnableCaching 模块,cache:annotation-driven 注解和 spring-context-support XML 元素都会自动启用 JCache 支持。spring-doc.cadn.net.cn

根据您的使用场景,选择权基本上在您手中。您甚至可以混合搭配服务:对某些服务使用 JSR-107 API,而对其他服务使用 Spring 自带的注解。然而,如果这些服务影响的是相同的缓存,您应采用一致且相同的键生成实现。