spring boot中自帶有數(shù)據(jù)緩存機(jī)制酥宴,主要通過(guò)其org.springframework.cache包下的各種類來(lái)實(shí)現(xiàn)啦吧。
EnableCaching
@EnableCaching是啟用緩存的注解,標(biāo)注在任何一個(gè)可自動(dòng)注入的類上即可開(kāi)啟拙寡。
Cacheable
@Cacheable是一個(gè)標(biāo)注與類與方法上的注解授滓,用于表示此類或此方法需要使用緩存機(jī)制。當(dāng)類與方法上都有時(shí),采用?就近原則褒墨。
在@Cacheable注解中,有一些常用參數(shù)可以進(jìn)行配置:
value與cacheNames?- 表示綁定的緩存名稱擎宝。這里的緩存指的是單個(gè)的緩存存儲(chǔ)器郁妈,并不是最終的鍵值對(duì)緩存對(duì)象。
key?- 表示緩存對(duì)象的?key绍申,這個(gè)才是最終的緩存鍵值對(duì)的?key噩咪。這里的參數(shù)需要使用?SpEL表達(dá)式。
keyGenerator?- 表示用于生成此方法?緩存key的類极阅。與key參數(shù)只能選擇一個(gè)添加胃碾,否則會(huì)拋出IllegalStateException異常。
cacheManager?- 指定緩存管理器筋搏。這個(gè)后面再細(xì)說(shuō)仆百。
condition?- 緩存的條件。支持SpEL奔脐,當(dāng)緩存條件滿足時(shí)俄周,才會(huì)進(jìn)入緩存取值模式。
unless?- 排除的條件髓迎。支持SpEL峦朗,當(dāng)排除的條件滿足時(shí),會(huì)直接調(diào)用方法取值排龄。
sync?- 異步緩存模式波势。是否采用異步的方式,在方法取值時(shí)異步緩存橄维。默認(rèn)false尺铣,在緩存完成后才返回值份氧。
一般情況下桑驱,可以這樣使用:
@RestController
@RequestMapping("cache")
@Cacheable(value = "cache", sync = true)
public class CacheController {
? ? @Cacheable(value = "hello", sync = true, keyGenerator = "myKeyGenerator")
? ? @GetMapping("hello")
? ? public String hello(String name) {
? ? ? ? System.out.println("name - " + name);
? ? ? ? return "hello " + name;
? ? }
? ? @GetMapping("hello2")
? ? public String hello2(@RequestParam(defaultValue = "1") Integer size, @RequestParam(defaultValue = "world") String name) {
? ? ? ? System.out.println("name - " + name);
? ? ? ? return "hello " + name;
? ? }
}
這里的CacheController被標(biāo)記上了@Cacheable(value = "cache", sync = true)收捣,表示其下的方法默認(rèn)使用名為cache的緩存存取器诀黍,并采用異步的方式進(jìn)行緩存處理闺魏。
hello方法上同樣添加了@Cacheable(value = "hello", sync = true, keyGenerator = "myKeyGenerator")姑躲,使得hello方法使用了獨(dú)立的緩存設(shè)置箫老,并通過(guò)myKeyGenerator的策略來(lái)生成?緩存key逞怨。
CachePut
將方法返回值存入到緩存中福澡,一般情況下是用在更新操作中,并于Cacheable與CacheEvict配合使用糯累。
CacheEvict
清除緩存值册踩,一般用在刪除或更新操作中暂吉,并于Cacheable與CachePut配合使用。
并且在CacheEvict注解中阎肝,多了兩個(gè)參數(shù):
allEntries?- 清除當(dāng)前value下的所有緩存风题。
beforeInvocation?- 在方法執(zhí)行前清除緩存嫉父。
示例代碼示例如下:
@Cacheable(value = "c", key = "123")
? ? @GetMapping("hello")
? ? public String hello(String name) {
? ? ? ? System.out.println("name - " + name);
? ? ? ? return "hello " + name;
? ? }
? ? @GetMapping("/put")
? ? @CachePut(value = "c", key = "123")
? ? public String put() {
? ? ? ? return "hello put";
? ? }
? ? @GetMapping("/evict")
? ? @CacheEvict(value = "c", key = "123")
? ? public String evict() {
? ? ? ? return "hello put";
? ? }
上述代碼中稽鞭,訪問(wèn)hello接口時(shí)引镊,會(huì)從c緩存存取器中取出key為123的緩存數(shù)值,沒(méi)有則會(huì)調(diào)用方法并進(jìn)行緩存吩抓。
訪問(wèn)put接口時(shí)疹娶,會(huì)將c緩存存取器中key為123的緩存值改為hello put伦连,沒(méi)有則進(jìn)行緩存惑淳。
訪問(wèn)evict接口時(shí),會(huì)將c緩存存取器中key為123的緩存值刪除移斩,此時(shí)訪問(wèn)hello接口會(huì)重新調(diào)用方法并進(jìn)行緩存向瓷。
CacheConfig
@CacheConfig作為類上的注解,目的是為了統(tǒng)一配置其下的方法緩存參數(shù)你稚,并設(shè)定共享緩存名入宦。
cacheNames?- 共享緩存名數(shù)組室琢。設(shè)定后表示此類下的方法緩存會(huì)依次從這些緩存存取器中取值落追,如果有轿钠,則取用緩存值;若沒(méi)有則調(diào)用方法取值症汹,并緩存值到設(shè)定的所有緩存存取器中背镇。
CacheManager
緩存管理器接口泽裳,用來(lái)做緩存管理的類。一般我們需要自定義緩存策略時(shí)胸囱,就是從CacheManager來(lái)入手的烹笔。
直接上實(shí)例:
@Component
public class MyCacheManager implements CacheManager, InitializingBean {
? ? private final Map<String, Cache> cacheMap;
? ? public MyCacheManager() {
? ? ? ? cacheMap = new HashMap<>();
? ? }
? ? @Override
? ? public Cache getCache(String name) {
? ? ? ? System.out.println("正在獲取緩存 - " + name);
? ? ? ? return cacheMap.computeIfAbsent(name, MyCache::new);
? ? }
? ? @Override
? ? public Collection<String> getCacheNames() {
? ? ? ? return cacheMap.keySet();
? ? }
? ? @Override
? ? public void afterPropertiesSet() throws Exception {
? ? ? ? System.out.println("say something!");
? ? }
}
CacheManager有兩個(gè)方法需要被實(shí)現(xiàn):
getCache(String)?- 獲取緩存存取器抛丽。這里的name其實(shí)就對(duì)應(yīng)了@Cacheable注解中的value與cacheName參數(shù)。
getCacheNames?- 獲取類中所有緩存的名稱集合柬帕。這主要是為了Spring內(nèi)部的統(tǒng)一管理需要陷寝。
因?yàn)?Spring采用了默認(rèn)替補(bǔ)策略,所以我們使用@Component或是通過(guò)@Bean自動(dòng)注入后爆安,默認(rèn)的緩存管理器就會(huì)切換成我們自定義的扔仓。如果我們自定義了兩個(gè)的話咖耘,可以通過(guò)@Primary來(lái)設(shè)定默認(rèn)管理器儿倒。
Cache
緩存存取器,用來(lái)管理緩存鍵值對(duì)的基本單元彻犁。
為了能對(duì)不同的緩存采用不同的存取策略汞幢,我們可以定制不同的Cache微谓,并通過(guò)自定義的CacheManager的getCache方法返回對(duì)應(yīng)的Cache。
舉個(gè)例子:
public final static class MyCache extends ConcurrentMapCache {
? ? public MyCache(String name) {
? ? ? ? super(name);
? ? }
? ? @Override
? ? public <T> T get(Object key, Class<T> type) {
? ? ? ? System.out.println("正在讀取 - " + key);
? ? ? ? return super.get(key, type);
? ? }
? ? @Override
? ? public <T> T get(Object key, Callable<T> valueLoader) {
? ? ? ? System.out.println("正在讀取 - " + key);
? ? ? ? return super.get(key, valueLoader);
? ? }
? ? @Override
? ? public ValueWrapper get(Object key) {
? ? ? ? System.out.println("正在讀取 - " + key);
? ? ? ? return super.get(key);
? ? }
}
這里的MyCache集成了ConcurrentMapCache疾宏,并對(duì)每次緩存值的獲取都進(jìn)行了控制臺(tái)輸出坎藐。
KeyGenerator
緩存key生成器哼绑,用于自定義規(guī)則緩存key的生成抖韩。
其接口的方法只有一個(gè):
public interface KeyGenerator {
? ? Object generate(Object target, Method method, Object... params);
}
一目了然,通過(guò)調(diào)用的目標(biāo)對(duì)象茂浮、目標(biāo)方法與方法入?yún)⑦M(jìn)行key的生成。這里不做過(guò)多贅述顽馋。
不過(guò)需要注意的是,由于不同類可能有同名同參數(shù)的方法竟稳,這里建議加上target.getClass().getName()來(lái)作為標(biāo)記熊痴,避免出現(xiàn)不希望的緩存映射果善。