SpringBoot2.x—SpringCache(2)使用

JAVA && Spring && SpringBoot2.x — 學(xué)習(xí)目錄

SpringBoot2.x—SpringCache(1)集成
SpringBoot2.x—SpringCache(2)使用
SpringBoot2.x—SpringCache(3) CacheManager源碼

在上文SpringBoot2.x集成SpringCache+Redis中我們學(xué)習(xí)了如何整合Redis+SpringCache堆生。而本文扮碧,將重點講述如何使用SpringCache進行透明化的緩存螺句。

在Spring3.1版本后,Spring框架提供了對緩存透明化應(yīng)用的支持缤削。緩存抽象允許使用各種緩存解決方案瘦棋,而對代碼的影響最小。

從Spring4.1開始鲜棠,通過支持JSR-107注釋和更多自定義選項肌厨,來改善緩存抽象。

1. 基于聲明式注釋的緩存

SpringCache是Service層的聲明式緩存豁陆。即無需與業(yè)務(wù)代碼耦合柑爸,通過注解完成緩存。

2.1 @Cacheable注解

@Cacheable的注解的處理流程如下圖:

@Cacheable的處理流程.png

可以使用@Cacheable用來劃分可緩存的方法盒音,即將結(jié)果存儲在緩存中的方法表鳍,以便在后續(xù)調(diào)用(使用相同的參數(shù))時,返回緩存中的值而無需實際執(zhí)行該方法祥诽。

  • 注釋聲明以最簡單的形式:注解屬性為CacheName譬圣。
@Cacheable("books")
public Book findBook(ISBN isbn) {...}
  • 支持多個CacheName。
@Cacheable({"books", "isbns"})
public Book findBook(ISBN isbn) {...}

2.2 @Cacheable注解屬性

調(diào)用者在調(diào)用方法時雄坪,會通過注解屬性自動的去緩存中進行查詢胁镐。那么我們需要指定cacheManager(CacheResolver)cacheNamekey(keyGenerator)盯漂,來確定去那個緩存管理器(Redis颇玷,ConcurrentHashMap等)進行查詢。而cacheName以及key會組裝成對應(yīng)的鍵就缆。

2.2.1 CacheManager和CacheResolver

  1. @CacheManager:對于使用多個緩存管理器的應(yīng)用程序帖渠,可以設(shè)置cacheMananger用于選擇哪種緩存管理器(redis,EhCache...)竭宰,非必需空郊,當(dāng)有多個才需要指定。
@Cacheable(cacheNames="books", cacheManager="anotherCacheManager") 
public Book findBook(ISBN isbn) {...}
  1. @CacheResolver:也可指定使用哪個緩存管理器切揭。需要通過實現(xiàn)org.springframework.cache.interceptor.CacheResolver接口來解析
@Cacheable(cacheResolver="runtimeCacheResolver") 
public Book findBook(ISBN isbn) {...}

cacheManager和cacheResolver參數(shù)是互斥的狞甚,同時指定這兩個參數(shù)會導(dǎo)致異常。因為實現(xiàn)CacheManager會忽略自定義的CacheResolver廓旬。

2.2.2 cacheName

CacheName屬性也是value屬性哼审,定義@Cacheable注解時,必須使用該屬性孕豹。即指定緩存的名字涩盾。使用默認(rèn)CacheManager屬性,以及使用默認(rèn)的key屬性(SimpleKey對象包含所有的參數(shù)值)励背。

2.2.3 key和KeyGenerator

  1. keyGenrator屬性

SpringCache默認(rèn)使用SimpleKeyGenerator春霍,默認(rèn)情況下將參數(shù)值作為鍵狰右,但是可能會導(dǎo)致key重復(fù)出現(xiàn)宗弯。

我們在整合SpringCache中自定義CacheGenerator谚赎,將類名:方法名作為key的一部分铲汪。

而后@Cacheable注解中瓤漏,指定自定義的KeyGenerator很魂。

    @Cacheable(value = "book2",keyGenerator = "keyGenerator")
    public Account getAccInfo(String customerId, String accType) {
      //業(yè)務(wù)邏輯
    }

注意key和keyGenerator依舊是互斥的仇祭。

  1. key屬性

當(dāng)然若是使用key屬性澡屡,也是可以指定類名和方法名等參數(shù)作為key炼蛤。

SpringCache提供了與緩存相關(guān)的專用元數(shù)據(jù)妖爷,例如參數(shù)名稱。下表描述了可用于上下文的項目理朋,以便于key的生成和條件計算絮识。

名稱 位置 描述 例子
methodMame root 被調(diào)用方法的名稱 #root.methodName
method root 被調(diào)用的方法 #root.method.name
target root 被調(diào)用的目標(biāo)對象 #root.target
targetClass root 被調(diào)用目標(biāo)的類 #root.targetClass
args root 用于被調(diào)用目標(biāo)的參數(shù)值(數(shù)組) #root.args[0]
caches root 執(zhí)行當(dāng)前方法緩存的集合 #root.caches[0].name
參數(shù)名稱 調(diào)用的方法 方法的任何參數(shù)名稱 #iban或#a0
result 調(diào)用的方法 僅用在unless,方法調(diào)用的結(jié)果(緩存值) #result
  1. cacheName無法使用SpEL表達式,#root.args是參數(shù)值嗽上。
    @Cacheable(cacheNames = "#root.methodName",key = "#root.args")
    public User getUser(int id) {
        User user = new User().setUserName("tom").setId(id);
        log.info("【調(diào)用getUser】方法");
        return user;
    }
圖4- @Cacheable(cacheNames = "#root.methodName",key = "#root.args").png
  1. 兩個SpEL表達式拼接次舌,創(chuàng)建更具體的key值
    @Cacheable(value = "book2",  
            key = "#root.targetClass.getSimpleName().concat(':').concat(#root.methodName).concat(':').concat(#customerId)")  
    public User getUser(int id) {
        User user = new User().setUserName("tom").setId(id);
        log.info("【調(diào)用getUser】方法");
        return user;
    }
圖5-兩個SpEL表達式拼接.png

2.3.4 同步緩存

在多線程環(huán)境下,某些操作可能會為一個參數(shù)并發(fā)調(diào)用兽愤。默認(rèn)情況下彼念,SpringCache不會鎖定任何內(nèi)容挪圾,并且可能多次計算相同的值,從而破壞了緩存的目的逐沙。

對于那些特殊情況哲思,可以使用sync屬性來鎖定。即只有一個線程正在忙于計算該值吩案,而其他線程則被阻塞棚赔,直到緩存中更新該條目為止。

@Cacheable(cacheNames="foos", sync=true) 
public Foo executeExpensiveOperation(String id) {...}

4. 條件緩存

  1. condition:方法可能不總適合緩存(例如:他可能取決于給你定的參數(shù))徘郭。緩存注釋通過condition支持這種功能靠益,該參數(shù)采用SpEL表達式,該表達式的值等于true或false残揉。如果為true胧后,則緩存該方法。否則的話抱环,每次調(diào)用該方法壳快。例如:僅當(dāng)參數(shù)name的長度小于32時才緩存以下方法:
@Cacheable(cacheNames="book", condition="#name.length() < 32") 
public Book findBook(String name)
  1. unless(如果不):可以使用unless參數(shù)來決定是否將值添加到緩存中,該參數(shù)也采用SpEl表達式江醇,該表達式輸出結(jié)果boolean類型濒憋。與condition不同的是何暇,unless表達式是在調(diào)用方法后求值的陶夜,并且當(dāng)SpEL返回false時,加入到緩存中(unless:如果不小于1000裆站,則存儲条辟。)。
    @Cacheable(cacheNames="books", key="#isbn.rawNumber",unless ="#result.id < 1000" )
    public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) {
        log.info("執(zhí)行方法宏胯!");
        Book book = Book.builder().id(1101).bookName("java").build();
        return book;
    }

若出現(xiàn)org.springframework.expression.spel.SpelEvaluationException: EL1008E異常原因

SpringCache支持java.util.Optional羽嫡,僅在支持時才將其內(nèi)容作為緩存。#result始終引用業(yè)務(wù)實體肩袍,而不引用受支持的包裝器杭棵。因此可以重寫為下面代碼:

    @Cacheable(cacheNames="books", key="#isbn.rawNumber",unless ="#result?.id >1000" )
    public Optional<Book> findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) {
        log.info("執(zhí)行方法!");
        Book book = Book.builder().id(111).bookName("java").build();
        Optional<Book> optionalBook = Optional.of(book);
        return optionalBook;
    }

請注意:result仍然指的是Book而不是Optional氛赐。

1. @CachePut注解

在不影響方法執(zhí)行的情況下更新緩存魂爪,可以使用@CachePut注解。也就是說艰管,該方法始終執(zhí)行滓侍,將其結(jié)果放入緩存(根據(jù)@CachePut選項)。他支持與@Cacheable緩存相同的屬性牲芋。但是它應(yīng)用于緩存填充而不是方法優(yōu)化撩笆。

@CachePut(cacheNames="book", key="#isbn")
public Book updateBook(ISBN isbn, BookDescriptor descriptor)
圖1-更新緩存.png

2.2 @CacheEvict注解

Spring Cache不僅允許緩存的填充捺球,還允許刪除緩存。此過程對于從緩存中刪除陳舊或未使用的數(shù)據(jù)很有用夕冲,相對于@Cacheable氮兵,@CacheEvict是從緩存中刪除數(shù)據(jù)的注解。@CacheEvict需要指定一個或多個受操作影響的緩存耘擂,允許自定義緩存和Key或者條件胆剧。

  1. allEntries,該參數(shù)指示是否需要在整個緩存范圍內(nèi)逐出而不僅僅是基于Key的逐條逐出醉冤。
    代碼1:逐出books緩存中的所有條目秩霍。
@CacheEvict(cacheNames="books", allEntries=true) 
public void loadBooks(InputStream batch)
  1. beforeInvocation,該參數(shù)指定逐出緩存是在方法執(zhí)行前還是方法執(zhí)行后(默認(rèn)方法執(zhí)行后)蚁阳。在默認(rèn)情況下铃绒,如果方法未執(zhí)行(可能已經(jīng)被緩存)或者引發(fā)異常,緩存是不會被移除的螺捐。而beforeInvocation=true逐出緩存則是在方法調(diào)用前發(fā)生颠悬。適用于移除操作和方法結(jié)果沒有必要聯(lián)系的情況。

代碼2:方法執(zhí)行前移除緩存

    @CacheEvict(cacheNames = "books", key = "#isbn",beforeInvocation = true)
    public void loadBooks(ISBN isbn) {
        log.info("清除緩存定血!");
        //出現(xiàn)異常赔癌,默認(rèn)不會清除緩存
        throw new RuntimeException("aa");
    }

注:void方法可以與@CacheEvict一起使用,因為方法充當(dāng)觸發(fā)器澜沟,返回值將被忽略(因為他們不與緩存交互)灾票。

2.3 Caching注解

指定多個相同類型的注解時(例如@CacheEvict或@CachePut)。因為Key或Key的表達式在不同的緩存間是不同的茫虽。@Caching允許嵌套多個@Cacheable刊苍,@CachePut和@CacheEvict注解來使用。

代碼3:使用兩個@CacheEvict注解

@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
public Book importBooks(String deposit, Date date)

2.4 CacheConfig注解

@CacheConfig是一個類級別的注解濒析,他允許共享cacheNames正什,custom KeyGenerator,custom CacheManager和custom CacheResolver号杏。將此注解注釋在類上不會打開任何緩存操作婴氮。

注:方法級別的注解會覆蓋類注解

本文推薦

Spring官網(wǎng)對Cache的描述

SpringBoot官網(wǎng)對Cache的描述

spring-boot的spring-cache中的擴展redis緩存的ttl和key名

歷史文章

mybatis&&數(shù)據(jù)庫優(yōu)化&&緩存目錄
JAVA && Spring && SpringBoot2.x 目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市盾致,隨后出現(xiàn)的幾起案子主经,更是在濱河造成了極大的恐慌,老刑警劉巖绰上,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旨怠,死亡現(xiàn)場離奇詭異,居然都是意外死亡蜈块,警方通過查閱死者的電腦和手機鉴腻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門迷扇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人爽哎,你說我怎么就攤上這事蜓席。” “怎么了课锌?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵厨内,是天一觀的道長。 經(jīng)常有香客問我渺贤,道長雏胃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任志鞍,我火速辦了婚禮瞭亮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘固棚。我一直安慰自己统翩,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布此洲。 她就那樣靜靜地躺著厂汗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪呜师。 梳的紋絲不亂的頭發(fā)上娶桦,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音匣掸,去河邊找鬼趟紊。 笑死氮双,一個胖子當(dāng)著我的面吹牛碰酝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播戴差,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼送爸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了暖释?” 一聲冷哼從身側(cè)響起袭厂,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎球匕,沒想到半個月后纹磺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡亮曹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年橄杨,在試婚紗的時候發(fā)現(xiàn)自己被綠了秘症。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡式矫,死狀恐怖乡摹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情采转,我是刑警寧澤聪廉,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站故慈,受9級特大地震影響板熊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜察绷,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一邻邮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧克婶,春花似錦筒严、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至筋岛,卻和暖如春娶视,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背睁宰。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工肪获, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柒傻。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓孝赫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親红符。 傳聞我的和親對象是個殘疾皇子青柄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一、前言 ??最近在和同事一塊做一個需求的時候预侯,看了下同事的代碼致开,在使用緩存這塊看到了Spring Cache這個...
    騎著烏龜去看海閱讀 4,739評論 0 9
  • 本文轉(zhuǎn)自被遺忘的博客 Spring Cache 緩存是實際工作中非常常用的一種提高性能的方法, 我們會在許多場景下...
    quiterr閱讀 1,080評論 0 8
  • 史上最全的Spring Boot Cache使用與整合 一:Spring緩存抽象 Spring從3.1開始定義了o...
    eloong閱讀 783評論 0 1
  • Spring Boot高級 內(nèi)容概要 一、Spring Boot與緩存 二萎馅、Spring Boot與消息 三双戳、Sp...
    順毛閱讀 373評論 0 2
  • 感悟:學(xué)會先去傾聽,很多時候我們對于事情都會有不同的見解和認(rèn)知度糜芳,先聽對方說完然后再從中換位思考飒货,最后再做出回應(yīng)千诬,...
    臨淄茂業(yè)DDM黃紅閱讀 135評論 0 0