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
用來劃分可緩存的方法盒音,即將結(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)
、cacheName
、key(keyGenerator)
盯漂,來確定去那個緩存管理器(Redis颇玷,ConcurrentHashMap等)進行查詢。而cacheName以及key會組裝成對應(yīng)的鍵就缆。
2.2.1 CacheManager和CacheResolver
- @CacheManager:對于使用多個緩存管理器的應(yīng)用程序帖渠,可以設(shè)置cacheMananger用于選擇哪種緩存管理器(redis,EhCache...)竭宰,非必需空郊,當(dāng)有多個才需要指定。
@Cacheable(cacheNames="books", cacheManager="anotherCacheManager")
public Book findBook(ISBN isbn) {...}
- @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
- 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依舊是互斥的仇祭。
- 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 |
- 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;
}
- 兩個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;
}
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. 條件緩存
- 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)
- 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)
2.2 @CacheEvict注解
Spring Cache不僅允許緩存的填充捺球,還允許刪除緩存。此過程對于從緩存中刪除陳舊或未使用的數(shù)據(jù)很有用夕冲,相對于@Cacheable氮兵,@CacheEvict是從緩存中刪除數(shù)據(jù)的注解。@CacheEvict需要指定一個或多個受操作影響的緩存耘擂,允許自定義緩存和Key或者條件胆剧。
-
allEntries
,該參數(shù)指示是否需要在整個緩存范圍內(nèi)逐出而不僅僅是基于Key的逐條逐出醉冤。
代碼1:逐出books緩存中的所有條目秩霍。
@CacheEvict(cacheNames="books", allEntries=true)
public void loadBooks(InputStream batch)
-
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-boot的spring-cache中的擴展redis緩存的ttl和key名
歷史文章
mybatis&&數(shù)據(jù)庫優(yōu)化&&緩存目錄
JAVA && Spring && SpringBoot2.x 目錄