- 从3.1开始,Spring 引入了对 Cache 的支持。其使用方法和原理都类似于 Spring 对事务管理的支持。
- Spring Cache 是作用在方法上的,当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时直接从缓存中获取结果进行返回。
所以在使用 Spring Cache 的时候我们要保证我们缓存的方法对于相同的方法参数要有相同的返回结果
- Spring为我们提供了几个注解来支持 Spring Cache。其核心主要是 @Cacheable 和 @CacheEvict。使用 @Cacheable 标记的方法在执行后 Spring Cache 将缓存其返回结果,而使用 @CacheEvict 标记的方法会在方法执行前或者执行后移除 Spring Cache 中的某些元素。下面我们将来详细介绍一下 Spring 基于注解对 Cache 的支持所提供的几个注解
注解 |
说明 |
||||||||||||||||||
@Cacheable |
产生缓存 指示一个方法(或类上的所有方法)触发缓存查询或更新操作,即优先使用缓存结果,有则跳过执行方法
当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。
对于支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果
|
||||||||||||||||||
@CachePut |
更新缓存 指示一个方法(或类上的所有方法)触发缓存更新操作。与@Cacheable注释不同,该注解不会在跳过已缓存的方法,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。如果condition()和unless()表达式匹配,它总是会调用方法并将其结果存储在关联的缓存中
|
||||||||||||||||||
@CacheEvict |
删除缓存 指示一个方法(或类上的所有方法)执行都触发缓存清除操作。该注释可以用作元注释,以创建带有属性覆盖的自定义组合注释
|
||||||||||||||||||
@CacheConfig |
提供一种在类级别共享公共缓存相关设置的机制。当此注解出现在给定的类上时,它会为该类中定义的任何缓存操作提供一组默认设置。
使用 @CacheConfig 注释,我们可以在类级别将一些缓存配置精简到一个位置,这样就不必多次声明
|
||||||||||||||||||
@Caching |
多个缓存注释(不同或相同类型)的组注释。即对@Cacheable @CachePut @CacheEvict的复合(combine)操作。此注解可用作元注解,以创建具有属性覆盖的自定义组合注解
|
||||||||||||||||||
@EnableCaching |
开启缓存 在类上设置当前缓存的一些公共设置,比如使用这个注解作用在类上,就可以为此类的所有方法的缓存注解提供默认值、缓存的默认名称、缓存的KeyGenerator
|
示例
@Service //开启缓存 @EnableCaching //共享的缓存设置 @CacheConfig(cacheNames={"addresses"}) public class RedisService { @Autowired UserInfoMapper userInfoMapper;
//将方法运行结果进行缓存,当方法被再次调用时,直接返回缓存中的结果。 @Cacheable(value = "test", key = "#id") public String getUserById(Integer id) { UserInfo userInfo=userInfoMapper.getUserById(id); return userInfo.toString(); }
//修改了数据库的数据,同时更新缓存。先调用目标方法,然后缓存方法结果。 @CachePut(value = "test",key="#result.id") //只能是result.id public UserInfo updateUser(UserInfo userInfo) { userInfoMapper.updateUser(userInfo); return userInfo; }
//删除数据之后,清除缓存 @CacheEvict(value = "test", key = "#id") public String deleteUser(Integer id) { userInfoMapper.deleteUserById(id); return "已删除"; }
//同时组合多个注解 @Caching( evict = { @CacheEvict("address"), @CacheEvict(value = "test1", key = "#id") }, put = { @CachePut(value = "test2",key="#result.id"), @CachePut(value = "test3",key="#result.name") }, cacheable = { @Cacheable(value = "test4", key = "#id"), @Cacheable(value = "test5", key = "#name"), } ) public String getAddress(UserInfo userInfo) … }
} |
注
- key 的生成策略有两种,一种是默认策略,一种是自定义策略。
key生成策略 |
说明 |
默认策略 |
通过 KeyGenerator 生成 |
自定义策略 |
通过Spring的EL表达式来指定 |
- 默认的 key 生成策略是通过 KeyGenerator 生成的,其默认策略如下:
- 如果方法没有参数,则使用 0 作为 key。
- 如果只有一个参数的话则使用该参数作为 key。
- 如果参数多余一个的话则使用所有参数的 hashCode 作为 key。
- 如果需要自定义默认生成策略,可以实现 KeyGenerator,然后指定 Spring Cache 使用的 KeyGenerator 为自定义实现的 KeyGenerator。
<cache:annotation-driven key-generator="userKeyGenerator"/>
<bean id="userKeyGenerator" class="com.xxx.cache.UserKeyGenerator"/>
- 或者基于XML配置的 <cache:advice> 来指定的。
<cache:advice id="cacheAdvice" cache-manager="cacheManager" key-generator="userKeyGenerator">
</cache:advice>
- 自定义策略是指可以通过 Spring 的 EL 表达式来指定 key。这里的 EL 表达式可以使用方法参数及它们对应的属性。使用方法参数时可以直接使用 “#参数名” 或者 “#p参数index”
使用 SpEL 表达式条件
- Spring Cache 提供了一些供我们使用的SpEL上下文数据:
名称 |
位置 |
描述 |
示例 |
methodName |
root对象 |
当前被调用的方法名 |
root.methodName |
method |
root对象 |
当前被调用的方法 |
root.method.name |
target |
root对象 |
当前被调用的目标对象 |
root.target |
targetClass |
root对象 |
当前被调用的目标对象类 |
root.targetClass |
args |
root对象 |
当前被调用的方法的参数列表 |
root.args[0] |
caches |
root对象 |
当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”, “cache2”})),则有两个cache |
root.caches[0].name |
argument name |
执行上下文 |
当前被调用的方法的参数,如findById(Long id),我们可以通过#id拿到参数 |
user.id |
result |
执行上下文 |
方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,’cache evict’的beforeInvocation=false) |
result |
示例
@Service
//开启缓存
@EnableCaching
public class RedisService {
@CacheEvict(value = "user",
key = "#user.id",
condition = "#root.target.canCache()
and
#root.caches[0].get(#user.id).get().username
ne
#user.username", beforeInvocation = true
)
public void conditionUpdate(User user) {
…
}
}