参考博客:
@CachePut、@Cacheable、@CacheEvict
@CachePut:被修饰的方法需要返回值,通常用于新增、修改操作。执行方法,将方法的执行结果放到缓存中
@Cacheable:被修饰的方法需要返回值,通常用于读操作。有缓存则用缓存;没缓存则执行方法,将方法的执行结果放入缓存。相当于getAndaddCacheIfAbsent
@CacheEvict:方法不需要返回值,通常用于删除操作。执行方法后删除缓存。
其中@CachePut、@Cacheable、@CacheEvict有以下的参数
参数 | 解释 | example |
---|---|---|
value/cacheNames | 缓存的名称 | |
key | 缓存的key值生成规则。可以为空,缺省时按照方法的所有参数进行组合;如果指定则要按照 SpEL 表达式编写 | |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | @CacheEvict(value=”testcache”,condition=”#userName.length()>2”) |
allEntries(@CacheEvict特有) | 是否清空所有缓存内容.缺省为 false,如果指定为 true,则方法调用后将立即清空所有cacheNames相同的缓存 | @CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation(@CacheEvict特有) | 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 | @CachEvict(value=”testcache”,beforeInvocation=true) |
redis中真正生成的key值是:cacheNames:key,如下代码
@Cacheable(cacheNames = "car", key = "#id")
@Override
public Car getById(String id) {
return = service.getById(id);
}
// 假设有数据:car {id: 1, name: 粤A 00000}
// 如果传入1,则会在redis里缓存 car:1 -> {id: 1, name: 粤A 00000}
缓存的名称,在 spring 配置文件中定义,必须指定至少一个 @CacheEvict(value=”my cache”)
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 @CacheEvict(value=”testcache”,key=”#userName”)
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 @CacheEvict(value=”testcache”,condition=”#userName.length()>2”)
allEntries ,
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 |
要注意的点
@Cacheable方法返回null
代码如下所示,当调用者传入不合法的id值,导致查询的结果为null,@Cacheable将方法的执行结果null缓存到redis,这样只要攻击者发大量的这种请求,就可以把redis内存撑爆。
@Cacheable(cacheNames = "car", key = "#id")
@Override
public Car getById(String id) {
return = service.getById(id);
}
解决办法
// unless会在方法执行后判断,所以可以获取到result
@Cacheable(cacheNames = "car", key = "#id", unless = "null == #result")
@Override
public Car getById(String id) {
return = service.getById(id);
}
虽然这样可以防止撑爆redis的内存,但是这样会有大量无效的请求打到数据库。这也就是缓存穿透,缓存穿透会在以后的博客中学习。