一、@Cacheable 注解
作用:将方法的运行结果进行缓存,以后再要相同的数据,直接从缓存中获取,不用调用方法:
属性:
value/cacheNames:指定缓存组件的名字;
CacheManager 管理多个 Cache 组件,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字
key: 缓存数据使用的 key,可以用它来指定。默认是使用方法参数的值。如 id=1, 1——>方法的返回值
使用SPEL表达式:#id 是参数 id 的值 #a0 #p0 #root.arg[0]
keyGenerator:key 的生成器,可以自己指定 key 的生成器的组件 id
key 与 keyGenerator 二选一使用
cacheManager:指定缓存管理器;或者是 cacheResolver
condition:判断条件,指定符合条件的情况下才缓存
unless:否定,unless 指定的条件为 true,方法的返回值就不会被缓存,可以获取到结果进行判断
unless = "#result == null"
sync:是否使用异步模式
代码示例:
@Cacheable(cacheNames = {"emp"})
public Employee getEmpById(Integer id) {
System.out.println("查询" + id +"号员工");
return employeeMapper.getEmpById(id);
}
二、@Cacheable 原理
当在主程序类上使用了 @EnableCaching 注解就可以开启基于注解的缓存,下面来分析一下缓存的原理。
1、自动配置类 CacheAutoConfiguration
给容器中导入 CacheConfigurationImportSelector 类。
2、CacheConfigurationImportSelector 给容器中导入一系列的缓存的配置类
导入的自动配置类:
3、查看那个配置类默认生效
在配置文件中使用 debug = true 打开配置报告
默认是 SimpleCacheConfiguration 缓存配置类生效。
4、SimpleCacheConfiguration 作用
SimpleCacheConfiguration 给容器中注册了一个CacheManager: ConcurrentMapCacheManager
5、ConcurrentMapCacheManager 作用
ConcurrentMapCacheManager 实现了 CacheManager 接口,并重写了其中的方法:
ConcurrentMapCacheManager 作用:
(1)可以获取和创建 ConcurrentMapCache 类型的缓存组件;
(2)他的作用是将数据保存到 ConcurrentMap 中;
6、ConcurrentMapCache 类
用于缓存数据的类,其中使用 ConcurrentMap 来缓存数据。
存放值的方法:
三、运行流程
以 @Cacheable 注解为例:
1、方法运行之前,先去查询 Cache(缓存组件),按照 cacheNames 指定的名字获取(CacheManager先获取相应的缓存),第一次获取缓存组件如果没有 Cache 组件会自动创建;
2、去 Cache 中查找缓存的内容,使用一个 key(默认就是方法的参数),key 是按照某种策略生成的 ,默认是使用 keyGenerator 生成的,默认使用 SimpleKeyGenerator 生成 key;
SimpleKeyGenerator 生成 key 的默认策略
如果没有参数:key = new SimpleKeyGenerator
如果有一个参数: key = 参数的值
如果有多个参数:key = new SimpleKey(param);
3、没有查到缓存就调用目标方法;
4、将目标方法返回的结果,放进缓存中
@Cacheable 标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为 key 去查询,如果没有就运行方法,并将结果放入缓存中,以后再来调用就可以直接使用缓存中的数据。
核心:
(1)使用 CacheManager【ConcurrentMapCacheManager】 按照名字得到 Cache 【ConcurrentMapCache】组件;
(2)key 是使用 keyGenerator 生成的,默认是 SimpleKeyGenerator 生成的;
四、常用属性
1、value/cacheNames
指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存
示例:
@Cacheable(cacheNames = {"emp", "temp"})
public Employee getEmpById(Integer id) {
System.out.println("查询" + id +"号员工");
return employeeMapper.getEmpById(id);
}
2、key: 缓存数据使用的 key
支持使用 Spel 表达式
使用SPEL表达式:#id 是参数 id 的值 #a0 #p0 #root.arg[0]
getEmp[1] 作为 key key = "#root.methodName + '[' + #id + ']'"
Spel 表达式:
3、keyGenerator:key 的生成器
key 与 keyGenerator 二选一使用,可以自定义keyGenerator
自定义 KeyGenerator:
@Configuration
public class MyCacheConfig {
@Bean(value = "myKeyGenerator")
public KeyGenerator keyGenerator() {
return new KeyGenerator(){
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName() + "[" + Arrays.asList(params).toString() + "]";
}
};
}
}
@Cacheable(cacheNames = {"emp"}, keyGenerator = "myKeyGenerator")
public Employee getEmpById(Integer id) {
System.out.println("查询" + id +"号员工");
return employeeMapper.getEmpById(id);
}
4、condition:判断条件,指定符合条件的情况下才缓存
condition = "#a0 > 1" 当第一个参数的值 > 1 的时候才进行缓存
condition = "#a0 > 1 and #root.methodName eq 'getEmpById'" 第一个参数的值 > 1 并且方法名是 getEmpById
5、unless:否定,unless 指定的条件为 true,方法的返回值就不会被缓存,可以获取到结果进行判断
unless = "#result == null" 方法返回值如果为null,结果不缓存
unless = "#a0 == 2 " 如果第一个参数的值是2,结果不缓存
示例:
@Cacheable(cacheNames = {"emp"}, keyGenerator = "myKeyGenerator", condition = "#a0 > 1", unless = "#a0 == 2 ")
public Employee getEmpById(Integer id) {
System.out.println("查询" + id +"号员工");
return employeeMapper.getEmpById(id);
}
6、sync:是否使用异步模式
#unless()} is not supported 开启异步,不支持 unless