SpringBoot緩存注解@Cacheable椿胯、@CacheEvict和@CachePut

一、簡述

從 3.1 開始魂爪,Spring 引入了對 Cache 的支持先舷。其使用方法和原理都類似于 Spring 對事務管理的支持。Spring Cache 是作用在方法上的滓侍,其核心思想:當調(diào)用一個緩存方法時蒋川,會把該方法參數(shù)和返回結果作為一個鍵值對存放在緩存中,等到下次利用同樣的參數(shù)來調(diào)用該方法時將不再執(zhí)行該方法撩笆,而是直接從緩存中獲取結果進行返回捺球。所以在使用 Spring Cache 的時候,要保證緩存的方法對于相同的方法參數(shù)要有相同的返回結果浇衬。使用 Spring Cache 需要做到兩點:

1??在 Springboot 的啟動類上使用 @EnableCaching 開啟緩存懒构。
2??聲明某些方法使用緩存。

和 Spring 對事務管理的支持一樣耘擂,Spring 對 Cache 的支持也有基于注解和基于 XML 配置兩種方式胆剧。

二、基于注解的支持

其核心主要是 @Cacheable 和 @CacheEvict醉冤。使用 @Cacheable 標記的方法在執(zhí)行后秩霍,Spring Cache 將緩存其返回結果,而使用 @CacheEvict 標記的方法會在方法執(zhí)行前或者執(zhí)行后移除 Spring Cache 中的某些元素蚁阳。

1??@Cacheable:可以標記在類上或者方法上铃绒。
當標記在方法上時表示該方法是支持緩存的,當標記在類上時則表示該類所有的方法都是支持緩存的螺捐。對于一個支持緩存的方法颠悬,Spring 會在其被調(diào)用后將其返回值緩存起來,以保證下次利用同樣的參數(shù)來執(zhí)行該方法時可以直接從緩存中獲取結果定血,而不需要再次執(zhí)行該方法赔癌。Spring 在緩存方法的返回值時是以鍵值對進行緩存的,值就是方法的返回結果澜沟,至于鍵的話灾票,Spring 又支持兩種策略,默認策略和自定義策略茫虽。需要注意的是當一個支持緩存的方法在對象內(nèi)部被調(diào)用時是不會觸發(fā)緩存功能的刊苍。@Cacheable 可以指定三個屬性既们,value、key 和 condition正什。

【value 屬性指定 Cache 名稱】
value 是必須指定的啥纸,其表示當前方法的返回值被緩存在哪個 Cache 上,對應 Cache 的名稱婴氮。其可以是一個 Cache 也可以是多個 Cache脾拆,當需要指定多個 Cache 時其是一個數(shù)組。

@Cacheable("cache1")//Cache是發(fā)生在cache1上的
public User find(Integer id) {
    return null;
}
@Cacheable({"cache1", "cache2"})//Cache是發(fā)生在cache1和cache2上的
public User find(Integer id) {
    return null;
}

【使用 key 屬性自定義 key】
key 屬性是用來指定 Spring 緩存方法的返回結果時對應的 key 的莹妒。該屬性支持 SpringEL 表達式。當沒有指定該屬性時绰上,Spring 將使用默認策略生成 key旨怠。
自定義策略是指可以通過 Spring 的 EL 表達式來指定 key。這里的 EL 表達式可以使用方法參數(shù)及它們對應的屬性蜈块。使用方法參數(shù)時可以直接使用#參數(shù)名或者#p參數(shù)index鉴腻。如下:

//key是指傳入時的參數(shù)
@Cacheable(value="users", key="#id")
public User find(Integer id) {
    return null;
}
// 表示第一個參數(shù)
@Cacheable(value="users", key="#p0")
public User find(Integer id) {
    return null;
}
// 表示User中的id值
@Cacheable(value="users", key="#user.id")
public User find(User user) {
    return null;
}
// 表示第一個參數(shù)里的id屬性值
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
   return null;
}

除了上述使用方法參數(shù)作為 key 之外牢撼,Spring 還提供了一個 root 對象可以用來生成 key量淌。通過該 root 對象可以獲取到以下信息。

當要使用 root 對象的屬性作為 key 時衷旅,也可以將“#root”省略器一,因為 Spring 默認使用的就是 root 對象的屬性课锌。如:

// key值為:user中的name屬性的值
@Cacheable(value={"users", "xxx"}, key="caches[1].name")
public User find(User user) {
    return null;
}

【condition 屬性指定發(fā)生的條件】
有時候可能并不希望緩存一個方法的所有返回結果。condition 屬性默認為空祈秕,表示將緩存所有的調(diào)用情形渺贤。其值是通過 SpringEL 表達式來指定的,當為 true 時表示進行緩存處理请毛;當為 false 時表示不進行緩存處理志鞍,即每次調(diào)用該方法時該方法都會執(zhí)行一次。如下表示只有當 user 的 id 為偶數(shù)時才會緩存:

// 根據(jù)條件判斷是否緩存
@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;
}

2??@CachePut
在支持 Spring Cache 的環(huán)境下方仿,對于使用 @Cacheable 標注的方法固棚,Spring 在每次執(zhí)行前都會檢查 Cache 中是否存在相同 key 的緩存元素,如果存在就不再執(zhí)行該方法仙蚜,而是直接從緩存中獲取結果進行返回此洲,否則才會執(zhí)行并將返回結果存入指定的緩存中。@CachePut 也可以聲明一個方法支持緩存功能鳍征。與 @Cacheable 不同的是使用 @CachePut 標注的方法在執(zhí)行前不會去檢查緩存中是否存在之前執(zhí)行過的結果黍翎,而是每次都會執(zhí)行該方法,并將執(zhí)行結果以鍵值對的形式存入指定的緩存中艳丛。

//@CachePut也可以標注在類或方法上匣掸√宋桑可以指定的屬性跟@Cacheable是一樣的
@CachePut("users")//每次都會執(zhí)行方法,并將結果存入指定的緩存中
public User find(Integer id) {
   return null;
}

3??@CacheEvict
@CacheEvict 是用來標注在需要清除緩存元素的方法或類上的碰酝。當標記在一個類上時表示其中所有的方法的執(zhí)行都會觸發(fā)緩存的清除操作霎匈。@CacheEvict 可以指定的屬性有 value、key送爸、condition铛嘱、allEntries 和 beforeInvocation。其中 value袭厂、key 和 condition 的語義與 @Cacheable 對應的屬性類似墨吓。即 value 表示清除操作是發(fā)生在哪些 Cache 上的(對應 Cache 的名稱);key 表示需要清除的是哪個 key纹磺,如未指定則會使用默認策略生成的 key帖烘;condition 表示清除操作發(fā)生的條件。
【allEntries 屬性】
allEntries 是 boolean 類型橄杨,表示是否需要清除緩存中的所有元素秘症。默認為 false,表示不需要式矫。當指定了 allEntries 為 true 時乡摹,Spring Cache 將忽略指定的 key。有的時候需要 Cache 一下清除所有的元素采转,這比一個一個清除元素更有效率聪廉。

@CacheEvict(value="users", allEntries=true)
public void delete(Integer id) {
   System.out.println("delete user by id: " + id);
}

【beforeInvocation 屬性】
清除操作默認是在對應方法成功執(zhí)行之后觸發(fā)的,如果方法拋出異常而未能成功返回時不會觸發(fā)清除操作故慈。使用b eforeInvocation 可以改變觸發(fā)清除操作的時間锄列,當指定該屬性值為 true 時,Spring 會在調(diào)用該方法之前清除緩存中的指定元素惯悠。

@CacheEvict(value="users", beforeInvocation=true)
public void delete(Integer id) {
   System.out.println("delete user by id: " + id);
}

其實除了使用 @CacheEvict 清除緩存元素外邻邮,當使用 Ehcache 作為實現(xiàn)時,也可以配置 Ehcache 自身的清除策略克婶,其是通過 Ehcache 的配置文件來指定的筒严。

4??@Caching
@Caching 注解可以在一個方法或者類上同時指定多個 Spring Cache 相關的注解。其擁有三個屬性:cacheable情萤、put 和 evict鸭蛙,分別用于指定 @Cacheable、@CachePut 和 @CacheEvict筋岛。

@Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),
@CacheEvict(value = "cache3", allEntries = true) })
public User find(Integer id) {
   return null;
}

5??使用自定義注解
Spring 允許在配置可緩存的方法時使用自定義的注解娶视,前提是自定義的注解上必須使用對應的注解進行標注。如有如下這么一個使用 @Cacheable 進行標注的自定義注解:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Cacheable(value="users")
public @interface MyCacheable {
}

那么在需要緩存的方法上使用 @MyCacheable 進行標注也可以達到同樣的效果。

@MyCacheable
public User findById(Integer id) {
    System.out.println("find user by id: " + id);
    User user = new User();
    user.setId(id);
    user.setName("Name" + id);
    return user;
}

三肪获、應用場景

1??@CachePut:更新數(shù)據(jù)庫的數(shù)據(jù)并更新緩存

  1. 先調(diào)用更新數(shù)據(jù)庫方法
  2. 再將更新的數(shù)據(jù)寫入名為(users)緩存中
@PostMapping(value = "/updateUserInfo")
@CachePut(value = "users", key = "#user.userId")
public void updateUserInfo(User user) {
   userService.updateUserInfo(user);
}

2??@Cahceable:查詢出的數(shù)據(jù)是最新的而不是之前緩存中的

  1. 這里 value 屬性的值同 @CachePut 時的 value 值一樣寝凌,確保查詢與更新都是同一塊緩存。
  2. 此時查詢的 key 要和更新的 key 保持一致孝赫,確保拿到的是更新后的數(shù)據(jù)较木。
@GetMapping(value = "/queryUserInfo/{userId}")
@Cacheable(value = "users", key = "#userId")
public User queryUserInfo(@PathVariable("userId")Integer userId) {
   userService.queryUserInfo(userId);
}
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市青柄,隨后出現(xiàn)的幾起案子伐债,更是在濱河造成了極大的恐慌,老刑警劉巖致开,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件峰锁,死亡現(xiàn)場離奇詭異,居然都是意外死亡双戳,警方通過查閱死者的電腦和手機祖今,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拣技,“玉大人,你說我怎么就攤上這事耍目「嘟铮” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵邪驮,是天一觀的道長莫辨。 經(jīng)常有香客問我,道長毅访,這世上最難降的妖魔是什么沮榜? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮喻粹,結果婚禮上蟆融,老公的妹妹穿的比我還像新娘。我一直安慰自己守呜,他們只是感情好型酥,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著查乒,像睡著了一般弥喉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上玛迄,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天由境,我揣著相機與錄音,去河邊找鬼蓖议。 笑死虏杰,一個胖子當著我的面吹牛讥蟆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嘹屯,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼攻询,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了州弟?” 一聲冷哼從身側響起钧栖,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎婆翔,沒想到半個月后拯杠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡啃奴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年潭陪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片最蕾。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡依溯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瘟则,到底是詐尸還是另有隱情黎炉,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布醋拧,位于F島的核電站慷嗜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏丹壕。R本人自食惡果不足惜庆械,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望菌赖。 院中可真熱鬧缭乘,春花似錦、人聲如沸琉用。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辕羽。三九已至逛尚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刁愿,已是汗流浹背绰寞。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滤钱。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓觉壶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親件缸。 傳聞我的和親對象是個殘疾皇子铜靶,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355