一、@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