@Cacheable
添加在方法上,缓存方法的执行结果。执行过程如下:
1.判断方法执行结果的缓存。如果有,则直接返回该缓存结果。
2.执行方法,获得方法结果。
3.根据是否满足缓存的条件。如果满足,则缓存方法结果到缓存。
4.返回方法结果。
常用属性:
cacheNames:缓存名。必填。[] 数组,可以填写多个缓存名。
values:和 cacheNames 属性相同,是它的别名。
key:缓存的 key 。允许空。
如果为空,则默认方法的所有参数进行组合。
如果非空,则需要按照 SpEL(Spring Expression Language) 来配置。例如说,@Cacheable(value = "users", key = "#id") ,使用方法参数 id 的值作为缓存的 key 。
SpEL(Spring Expression Language) 配置:
名字 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root object | 当前被调用的方法名 | #root.methodName |
method | root object | 当前被调用的方法 | #root.method.name |
target | root object | 当前被调用的目标对象 | #root.target |
targetClass | root object | 当前被调用的目标对象类 | #root.targetClass |
args | root object | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root object | 当前方法调用使用的缓存列表(如@Cacheable(value={"cache1","cache2"}),则有两个cache) | #root.caches[0].name |
argument name | evaluation context | 方法参数的名字,可以直接使用#参数名,也可以使用 #p0 或 #a0 的形式,0代表参数的索引 | #a0 |
result | evaluation context | 方法执行后的返回值(仅当方法执行后的判断有效) | #result |
condition:基于方法入参,判断要缓存的条件,允许空。
如果为空,则不进行入参的判断。
如果非空,则需要按照 SpEL(Spring Expression Language) 来配置。例如说,@Cacheable(condition="#id > 0") ,需要传入的 id 大于零。下面例子中#user.id%2==0,id为偶数时才对返回对象user进行缓存。
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
public User find(User user) {
System.out.println("find user by user " + user);
return user;
}
unless:基于方法返回,判断不缓存的条件。允许空。
如果为空,则不进行入参的判断。
如果非空,则需要按照 SpEL(Spring Expression Language) 来配置。例如说,@Cacheable(unless="#result == null") ,如果返回结果为 null ,则不进行缓存。
要注意,condition 和 unless 都是条件属性,差别在于前者针对入参,后者针对结果。
不常用属性:
keyGenerator:自定义 key 生成器 KeyGenerator Bean 的名字。允许空。如果设置,则 key 失效。
cacheManager:自定义缓存管理器 CacheManager Bean 的名字。允许空。一般不填写,除非有多个 CacheManager Bean 的情况下。
cacheResolver:自定义缓存解析器 CacheResolver Bean 的名字。允许空。
sync:在获得不到缓存的情况下,是否同步执行方法。
默认为 false ,表示无需同步。
如果设置为 true ,则执行方法时,会进行加锁,保证同一时刻,有且仅有一个方法在执行,其它线程阻塞等待。通过这样的方式,避免重复执行方法。注意,该功能的实现,需要参考第三方缓存的具体实现。
@CachePut
添加在方法上,缓存方法的执行结果。不同于 @Cacheable 注解,它的执行过程如下:
1.执行方法,获得方法结果。也就是说,无论是否有缓存,都会执行方法。
2.根据是否满足缓存的条件。如果满足,则缓存方法结果到缓存。
3.返回方法结果。
注解的属性,和 @Cacheable 注解的属性,基本一致,只少一个 sync 属性。
一般来说,使用方式如下:
@Cacheable
搭配读操作,实现缓存的被动写。
@Override
@Cacheable(value = {"menuById"}, key = "#id")
public Menu findById(String id) {
Menu menu = this.getById(id);
if (menu != null){
System.out.println("menu.name = " + menu.getName());
}
return menu;
}
@CachePut
配置写操作,实现缓存的主动写。
每次都会触发真实方法的调用,根据返回值来更新缓存,所以如果更新方法返回值为void,则缓存为Null;如果是Boolean、Integer等其他非对象返回值,则会报错无法转换成xxx对象。
@Override
@CachePut(value = "menuById", key = "#menu.id")
public Menu ReviseById(Menu menu) {
this.updateById(menu);
return menu;
}
@CacheEvict
添加在方法上,删除缓存,一般用在更新或者删除的方法上。
相比 @CachePut 注解,它额外多了两个属性:
allEntries 属性,是否删除缓存名( cacheNames )下,所有 key 对应的缓存。默认为 false ,只删除指定 key 的缓存。
beforeInvocation 属性,是否在方法执行前删除缓存。默认为 false ,在方法执行后删除缓存。
删除某个分区下的所有数据
@CacheEvict(value = {"name1"}, allEntries = true)
删除多个Key的缓存
@Cacheable(cacheNames = "provinceId:info",key ="#provinceId" )
public List<AreaDTO> findCityList(String provinceId) {
return areaRepository.findAllByParentIdAndDelFlagIsFalse(provinceId);
}
@Cacheable(cacheNames = "cityId:info",key ="#cityId" )
public List<AreaDTO> findOrganyList(String cityId) {
return areaRepository.findAllByParentIdAndDelFlagIsFalse(cityId);
}
@Cacheable(cacheNames = "organId:info",key ="#organId" )
public List<AreaDTO> findStreetList(String organId) {
return areaRepository.findAllByParentIdAndDelFlagIsFalse(organId);
}
@Cacheable(cacheNames = "areaCode:info",key = "#code")
public String findByCode(String code) {
Area area = areaRepository.findFirstByCodeAndDelFlagIsFalse(code);
if (area != null && StringUtils.isNotEmpty(area.getCode())) {
return area.getName();
}
return code;
}
@Caching(evict ={
@CacheEvict(cacheNames = "provinceId:info",allEntries = true),
@CacheEvict(cacheNames = "organId:info",allEntries = true),
@CacheEvict(cacheNames = "areaCode:info",allEntries = true),
@CacheEvict(cacheNames = "cityId:info",allEntries = true)
})
public void refresh() {
}
@Caching
添加在方法上,可以组合使用多个 @Cacheable、@CachePut、@CacheEvict 注解。不太常用,可以暂时忽略。
@CacheConfig
添加在类上,共享如下四个属性的配置:
cacheNames
keyGenerator
cacheManager
cacheResolver
@EnableCaching
标记开启 Spring Cache 功能,所以一定要添加。代码如下:
// EnableCaching.java
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
使用缓存带来的问题
高并发下,包括缓存穿透、缓存击穿、缓存雪崩、双写不一致等问题
- 缓存穿透:大量查询一个null的数据。解决:缓存空数据,配置文件添加配置: cache-null-values: true
- 缓存雪崩:大量key同时过期。解决:加随机时间;其实只要加上过期时间就可以满足要求 ,配置文件添加配置:time-to-live: 1000
- 双写不一致:这是一个比较常见的问题,其中一个常用的解决方案是,更新的时候,先删除缓存,再更新数据库。所以Spring Cache的@CacheEvict会有一个beforeInvocation的配置。
注解使用注意事项
spring-cache 的注解是基于spring aop代理类, 同一个类中方法A调用方法B, 方法B添加了注解,是不生效的。
//不生效
public void A(String key) {
// do something...
//不生效
this.B();
}
@Cacheable(cacheNames = "myCache")
public void B(String key) {
// do something...
}
文章评论