Spring系列快取註解@Cacheable @CacheEvit @CachePut 使用姿勢介紹
SpringBoot系列快取註解@Cacheable @CacheEvit @CachePut使用姿勢介紹
Spring在3.1版本,就提供了一條基於註解的快取策略,實際使用起來還是很絲滑的,本文將針對幾個常用的註解進行簡單的介紹說明,有需要的小夥伴可以嘗試一下
本文主要知識點:
- @Cacheable: 快取存在,則使用快取;不存在,則執行方法,並將結果塞入快取
- @CacheEvit: 失效快取
- @CachePut: 更新快取
I. 專案環境
1. 專案依賴
本專案藉助SpringBoot 2.2.1.RELEASE
+ maven 3.5.3
+ IDEA
+ redis5.0
進行開發
開一個web服務用於測試
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies>
全程使用預設配置,redis本機,埠6379,無密碼
II. 快取註解介紹
1. @Cacheable
這個註解用於修飾方法or類,當我們訪問它修飾的方法時,優先從快取中獲取,若快取中存在,則直接獲取快取的值;快取不存在時,執行方法,並將結果寫入快取
這個註解,有兩個比較核心的設定
/** * 與 cacheNames 效果等價 */ @AliasFor("cacheNames") String[] value() default {}; @AliasFor("value") String[] cacheNames() default {}; /** * 快取key */ String key() default "";
cacheNames可以理解為快取key的字首,可以為元件快取的key變數;當key不設定時,使用方法引數來初始化,注意key為SpEL表示式,因此如果要寫字串時,用單引號括起來
一個簡單的使用姿勢
/**
* 首先從快取中查,查到之後,直接返回快取資料;否則執行方法,並將結果快取
* <p>
* redisKey: cacheNames + key 組合而成 --> 支援SpEL
* redisValue: 返回結果
*
* @param name
* @return
*/
@Cacheable(cacheNames = "say", key = "'p_'+ #name")
public String sayHello(String name) {
return "hello+" + name + "-->" + UUID.randomUUID().toString();
}
如我們傳參為 yihuihui, 那麼快取key為 say::p_yihuihui
除了上面三個配置值之外,檢視@Cacheable
註解原始碼的童鞋可以看到還有condition
設定,這個表示當它設定的條件達成時,才寫入快取
/**
* 滿足condition條件的才寫入快取
*
* @param age
* @return
*/
@Cacheable(cacheNames = "condition", key = "#age", condition = "#age % 2 == 0")
public String setByCondition(int age) {
return "condition:" + age + "-->" + UUID.randomUUID().toString();
}
上面這個case中,age為偶數的時候,才走快取;否則不寫快取
接下來是unless
引數,從名字上可以看出它表示不滿足條件時才寫入快取
/**
* unless, 不滿足條件才寫入快取
*
* @param age
* @return
*/
@Cacheable(cacheNames = "unless", key = "#age", unless = "#age % 2 == 0")
public String setUnless(int age) {
return "unless:" + age + "-->" + UUID.randomUUID().toString();
}
2. @CachePut
不管快取有沒有,都將方法的返回結果寫入快取;適用於快取更新
/**
* 不管快取有沒有,都寫入快取
*
* @param age
* @return
*/
@CachePut(cacheNames = "t4", key = "#age")
public String cachePut(int age) {
return "t4:" + age + "-->" + UUID.randomUUID().toString();
}
3. @CacheEvict
這個就是我們理解的刪除快取
/**
* 失效快取
*
* @param name
* @return
*/
@CacheEvict(cacheNames = "say", key = "'p_'+ #name")
public String evict(String name) {
return "evict+" + name + "-->" + UUID.randomUUID().toString();
}
4. @Caching
在實際的工作中,經常會遇到一個數據變動,更新多個快取的場景,對於這個場景,可以通過@Caching
來實現
/**
* caching實現組合,新增快取,並失效其他的快取
*
* @param age
* @return
*/
@Caching(cacheable = @Cacheable(cacheNames = "caching", key = "#age"), evict = @CacheEvict(cacheNames = "t4", key = "#age"))
public String caching(int age) {
return "caching: " + age + "-->" + UUID.randomUUID().toString();
}
上面這個就是組合操作
- 從
caching::age
快取取資料,不存在時執行方法並寫入快取; - 失效快取
t4::age
5. 異常時,快取會怎樣?
上面的幾個case,都是正常的場景,當方法丟擲異常時,這個快取表現會怎樣?
/**
* 用於測試異常時,是否會寫入快取
*
* @param age
* @return
*/
@Cacheable(cacheNames = "exception", key = "#age")
@Cacheable(cacheNames = "say", key = "'p_yihuihui'")
public int exception(int age) {
return 10 / age;
}
根據實測結果,當age==0
時,上面兩個快取都不會成功
6. 測試用例
接下來驗證下快取註解與上面描述的是否一致
@RestController
public class IndexRest {
@Autowired
private BasicDemo helloService;
@GetMapping(path = {"", "/"})
public String hello(String name) {
return helloService.sayHello(name);
}
}
上面這個主要是驗證@Cacheable
註解,若快取不命中,每次返回的結果應該都不一樣,然而實際訪問時,會發現返回的都是相同的
curl http://localhost:8080/?name=yihuihui
失效快取
@GetMapping(path = "evict")
public String evict(String name) {
return helloService.evict(String.valueOf(name));
}
失效快取,需要和上面的case配合起來使用
curl http://localhost:8080/evict?name=yihuihui
curl http://localhost:8080/?name=yihuihui
剩下其他的相關測試類就比較好理解了,一併貼出對應的程式碼
@GetMapping(path = "condition")
public String t1(int age) {
return helloService.setByCondition(age);
}
@GetMapping(path = "unless")
public String t2(int age) {
return helloService.setUnless(age);
}
@GetMapping(path = "exception")
public String exception(int age) {
try {
return String.valueOf(helloService.exception(age));
} catch (Exception e) {
return e.getMessage();
}
}
@GetMapping(path = "cachePut")
public String cachePut(int age) {
return helloService.cachePut(age);
}
7. 小結
最後管理小結一下Spring提供的幾個快取註解
@Cacheable
: 快取存在,則從快取取;否則執行方法,並將返回結果寫入快取@CacheEvit
: 失效快取@CachePut
: 更新快取@Caching
: 都註解組合
上面雖說可以滿足常見的快取使用場景,但是有一個非常重要的點沒有說明,快取失效時間應該怎麼設定???
如何給每個快取設定不同的快取失效時間,咱麼下篇博文見,我是一灰灰,歡迎關注長草的公眾號一灰灰blog
III. 不能錯過的原始碼和相關知識點
0. 專案
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 原始碼:https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/125-cache-ano
1. 一灰灰Blog
盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛
- 一灰灰Blog個人部落格 https://blog.hhui.top
- 一灰灰Blog-Spring專題部落格 http://spring.hhui.top