JetCache?緩存框架的使用及源碼解析(推薦)
一、簡(jiǎn)介
JetCache
是一個(gè)基于Java的緩存系統(tǒng)封裝,提供統(tǒng)一的API和注解來(lái)簡(jiǎn)化緩存的使用。 JetCache提供了比SpringCache更加強(qiáng)大的注解,可以原生的支持TTL、兩級(jí)緩存、分布式自動(dòng)刷新,還提供了Cache
接口用于手工緩存操作。 當(dāng)前有四個(gè)實(shí)現(xiàn):RedisCache
、RedisLettuceCache
、CaffeineCache
、LinkedHashMapCache
。
特性:
- 通過(guò)統(tǒng)一的API訪問(wèn)Cache系統(tǒng)
- 通過(guò)注解實(shí)現(xiàn)聲明式的方法緩存,支持TTL和兩級(jí)緩存
- 通過(guò)注解創(chuàng)建并配置
Cache
實(shí)例 - 針對(duì)所有
Cache
實(shí)例和方法緩存的自動(dòng)統(tǒng)計(jì) - Key的生成策略和Value的序列化策略支持自定義配置
- 分布式緩存自動(dòng)刷新,分布式鎖
- 異步Cache API (使用Redis的Lettuce客戶端時(shí))
緩存類型:
- 本地
LinkedHashMap
:使用LinkedHashMap做LUR方式淘汰Caffeine
:基于Java8開發(fā)的提供了近乎最佳命中率的高性能的緩存庫(kù)
- 遠(yuǎn)程(訪問(wèn)Redis的客戶端)
Redis
:使用Jedis客戶端,Redis官方首選的Java客戶端RedisSpringData
:使用SpringData訪問(wèn)Redis(官網(wǎng)未作介紹)RedisLettuce
:使用Lettuce客戶端,一個(gè)高性能基于Java的Redis驅(qū)動(dòng)框架,支持線程安全的同步、異步操作,底層集成了Project Reactor,提供反應(yīng)式編程,參考:關(guān)于SpringBoot整合redis使用Lettuce客戶端超時(shí)問(wèn)題
為什么使用緩存?
在高并發(fā)、大流量等場(chǎng)景下,降低系統(tǒng)延遲,緩解數(shù)據(jù)庫(kù)壓力,提高系統(tǒng)整體的性能,讓用戶有更好的體驗(yàn)。
使用場(chǎng)景
讀多寫少、不追求強(qiáng)一致性、請(qǐng)求入?yún)⒉灰鬃兓?/p>
使用規(guī)范
選擇了遠(yuǎn)程緩存請(qǐng)?jiān)O(shè)置keyPrefix,保證存放至Redis的緩存key規(guī)范化,避免與其他系統(tǒng)出現(xiàn)沖突,例如這樣設(shè)計(jì):系統(tǒng)簡(jiǎn)稱:所屬名字:
,這樣存儲(chǔ)到Redis的緩存key為:系統(tǒng)簡(jiǎn)稱:所屬名字:緩存key
選擇了本地緩存請(qǐng)?jiān)O(shè)置limit,全局默認(rèn)設(shè)置了100,本地緩存的數(shù)據(jù)存放于內(nèi)存,減輕內(nèi)存的損耗,如果使用了Caffeine,緩存的key過(guò)多可能導(dǎo)致內(nèi)存溢出
請(qǐng)勿濫用緩存注解,對(duì)于非必要添加緩存的方法我們盡量不使用緩存
二、如何使用
說(shuō)明:以下使用方式是基于SpringBoot引入JetCache
緩存框架的,如果不是SpringBoot工程,請(qǐng)參考JetCache
官網(wǎng)使用
引入maven依賴
<dependencies> <!-- 使用 jedis 客戶端添加以下依賴 --> <dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-starter-redis</artifactId> <version>${version}</version> </dependency> <!-- 使用 lettuce 客戶端添加以下依賴 --> <dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-starter-redis-lettuce</artifactId> <version>${version}</version> </dependency> </dependencies>
添加配置
jetcache: statIntervalMinutes: 60 areaInCacheName: false penetrationProtect: false enableMethodCache: true hiddenPackages: com.xxx.xxx,com.xxx.xxx local: default: type: caffeine # 支持的類型:linkedhashmap、caffeine limit: 100 keyConvertor: fastjson # 支持的類型:fastjson,可自定義轉(zhuǎn)換器函數(shù) expireAfterWriteInMillis: 600000 expireAfterAccessInMillis: 300000 remote: default: type: redis.lettuce # 支持的類型:redis、redis.lettuce keyPrefix: '系統(tǒng)簡(jiǎn)稱:所屬名字:' keyConvertor: fastjson valueEncoder: java # 支持的類型:kryo、java,可自定義編碼器 valueDecoder: java # 支持的類型:kryo、java,可自定義解碼器 expireAfterWriteInMillis: 3600000 #readFrom: slavePreferred # 優(yōu)先從Slave節(jié)點(diǎn)中讀取 uri: redis-sentinel://host1:26379,host2:26379,host3:26379/?sentinelMasterId=mymaster # 哨兵模式 #uri: redis://127.0.0.1:6379/ # 單節(jié)點(diǎn)模式 #mode: masterslave # 設(shè)置為主從模式 #uri: # 集群模式 #- redis://127.0.0.1:7000 #- redis://127.0.0.1:7001 #- redis://127.0.0.1:7002 example: keyPrefix: '系統(tǒng)簡(jiǎn)稱:所屬名字:' type: redis keyConvertor: fastjson valueEncoder: java valueDecoder: java expireAfterWriteInMillis: 3600000 poolConfig: minIdle: 10 maxIdle: 20 maxTotal: 50 #password: xxx # 連接密碼 #timeout: 2000 # 連接的超時(shí)時(shí)間,讀取數(shù)據(jù)的超時(shí)時(shí)間 #database: 0 # 連接的數(shù)據(jù)庫(kù) #clientName: null # 客戶端名稱 #ssl: 是否使用SSL host: ${redis.host} port: ${redis.port} #sentinel: host1:26379,host2:26379,host3:26379 # 哨兵模式 #masterName: mymaster
配置說(shuō)明
jetcache的全局配置
屬性 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|
jetcache.statIntervalMinutes | 0 | 用于統(tǒng)計(jì)緩存調(diào)用相關(guān)信息的統(tǒng)計(jì)間隔(分鐘),0表示不統(tǒng)計(jì)。 |
jetcache.areaInCacheName | true | 緩存實(shí)例名稱cacheName會(huì)作為緩存key的前綴,2.4.3以前的版本總是把a(bǔ)reaName加在cacheName中,因此areaName也出現(xiàn)在key前綴中。我們一般設(shè)置為false。 |
jetcache.penetrationProtect | false | 當(dāng)緩存訪問(wèn)未命中的情況下,對(duì)并發(fā)進(jìn)行的加載行為進(jìn)行保護(hù)。 當(dāng)前版本實(shí)現(xiàn)的是單JVM內(nèi)的保護(hù),即同一個(gè)JVM中同一個(gè)key只有一個(gè)線程去加載,其它線程等待結(jié)果。這是全局配置,如果緩存實(shí)例沒(méi)有指定則使用全局配置。 |
jetcache.enableMethodCache | true | 是否使用jetcache緩存。 |
jetcache.hiddenPackages | 無(wú) | 自動(dòng)生成緩存實(shí)例名稱時(shí),為了不讓名稱太長(zhǎng),hiddenPackages指定的包名前綴會(huì)被截掉,多個(gè)包名使用逗號(hào)分隔。我們一般會(huì)指定每個(gè)緩存實(shí)例的名稱。 |
本地緩存的全局配置
屬性 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|
jetcache.local.${area}.type | 無(wú) | 本地緩存類型,支持 linkedhashmap、caffeine。 |
jetcache.local.${area}.limit | 100 | 每個(gè)緩存實(shí)例存儲(chǔ)的緩存數(shù)量的全局配置,僅本地緩存需要配置,如果緩存實(shí)例沒(méi)有指定則使用全局配置,請(qǐng)結(jié)合實(shí)例的業(yè)務(wù)場(chǎng)景進(jìn)行配置該參數(shù)。 |
jetcache.local.${area}.keyConvertor | 無(wú) | 緩存key轉(zhuǎn)換器的全局配置,支持的類型:fastjson 。僅當(dāng)使用@CreateCache且緩存類型為L(zhǎng)OCAL時(shí)可以指定為none ,此時(shí)通過(guò)equals方法來(lái)識(shí)別key。方法緩存必須指定keyConvertor。支持自定義轉(zhuǎn)換器函數(shù),可設(shè)置為:bean:beanName ,然后會(huì)從spring容器中獲取該bean。 |
jetcache.local.${area}.expireAfterWriteInMillis | 無(wú)窮大 | 本地緩存超時(shí)時(shí)間的全局配置(毫秒)。 |
jetcache.local.${area}.expireAfterAccessInMillis | 0 | 多長(zhǎng)時(shí)間沒(méi)訪問(wèn)就讓緩存失效的全局配置(毫秒),僅支持本地緩存。0表示不使用這個(gè)功能。 |
遠(yuǎn)程緩存的全局配置
屬性 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|
jetcache.remote.${area}.type | 無(wú) | 連接Redis的客戶端類型,支持 redis 、redis.lettuce 、redis.springdata 。 |
jetcache.remote.${area}.keyPrefix | 無(wú) | 保存至遠(yuǎn)程緩存key的前綴,請(qǐng)規(guī)范使用。 |
jetcache.remote.${area}.keyConvertor | 無(wú) | 參考上述說(shuō)明。 |
jetcache.remote.${area}.valueEncoder | java | 保存至遠(yuǎn)程緩存value的編碼函數(shù),支持:java 、kryo 。支持自定義編碼函數(shù),可設(shè)置為:bean:beanName ,然后會(huì)從spring容器中獲取該bean。 |
jetcache.remote.${area}.valueDecoder | java | 保存至遠(yuǎn)程緩存value的解碼函數(shù),支持:java 、kryo 。支持自定義解碼函數(shù),可設(shè)置為:bean:beanName ,然后會(huì)從spring容器中獲取該bean。 |
jetcache.remote.${area}.expireAfterWriteInMillis | 無(wú)窮大 | 遠(yuǎn)程緩存超時(shí)時(shí)間的全局配置(毫秒)。 |
jetcache.remote.${area}.uri | 無(wú) | redis節(jié)點(diǎn)信息。 |
上表中${area}對(duì)應(yīng)@Cached和@CreateCache的area屬性,如果注解上沒(méi)有指定area,默認(rèn)值是"default"。
關(guān)于緩存的超時(shí)時(shí)間:
- put等方法上指定了超時(shí)時(shí)間,則以此時(shí)間為準(zhǔn);
- put等方法上未指定超時(shí)時(shí)間,使用Cache實(shí)例的默認(rèn)超時(shí)時(shí)間;
- Cache實(shí)例的默認(rèn)超時(shí)時(shí)間,通過(guò)在@CreateCache和@Cached上的expire屬性指定,如果沒(méi)有指定,使用yml中定義的全局配置,例如@Cached(cacheType=local)使用jetcache.local.default.expireAfterWriteInMillis,如果仍未指定則是無(wú)窮大。
注解說(shuō)明
如果需要使用jetcache
緩存,啟動(dòng)類添加兩個(gè)注解:@EnableCreateCacheAnnotation
、@EnableMethodCache
@EnableCreateCacheAnnotation
開啟可通過(guò)@CreateCache注解創(chuàng)建Cache實(shí)例功能。
@EnableMethodCache
開啟可通過(guò)@Cached注解創(chuàng)建Cache實(shí)例功能,初始化spring aop,注解說(shuō)明:
屬性 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|
basePackages | 無(wú) | jetcache需要攔截的包名,只有這些包名下的Cache實(shí)例才會(huì)生效 |
order | Ordered.LOWEST_PRECEDENCE | 指定AOP切面執(zhí)行過(guò)程的順序,默認(rèn)最低優(yōu)先級(jí) |
mode | AdviceMode.PROXY | Spring AOP的模式,目前就提供默認(rèn)值讓你修改 |
proxyTargetClass | false | 無(wú) |
@Cached
為一個(gè)方法添加緩存,創(chuàng)建對(duì)應(yīng)的緩存實(shí)例,注解可以添加在接口或者類的方法上面,該類必須是spring bean,注解說(shuō)明:
屬性 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|
area | "default" | 如果在配置中配置了多個(gè)緩存area,在這里指定使用哪個(gè)area。 |
name | 未定義 | 指定緩存實(shí)例名稱,如果沒(méi)有指定,會(huì)根據(jù)類名+方法名自動(dòng)生成。name會(huì)被用于遠(yuǎn)程緩存的key前綴。另外在統(tǒng)計(jì)中,一個(gè)簡(jiǎn)短有意義的名字會(huì)提高可讀性。 |
enabled | true | 是否激活緩存。 |
timeUnit | TimeUnit.SECONDS | 指定expire的單位。 |
expire | 未定義 | 超時(shí)時(shí)間。如果注解上沒(méi)有定義,會(huì)使用全局配置,如果此時(shí)全局配置也沒(méi)有定義,則為無(wú)窮大。 |
localExpire | 未定義 | 僅當(dāng)cacheType為BOTH時(shí)適用,為本地緩存指定一個(gè)不一樣的超時(shí)時(shí)間,通常應(yīng)該小于expire。如果沒(méi)有設(shè)置localExpire且cacheType為BOTH,那么本地緩存的超時(shí)時(shí)間和遠(yuǎn)程緩存保持一致。 |
cacheType | CacheType.REMOTE | 緩存的類型,支持:REMOTE 、LOCAL 、BOTH ,如果定義為BOTH,會(huì)使用LOCAL和REMOTE組合成兩級(jí)緩存。 |
localLimit | 未定義 | 如果cacheType為L(zhǎng)OCAL或BOTH,這個(gè)參數(shù)指定本地緩存的最大元素?cái)?shù)量,以控制內(nèi)存占用。如果注解上沒(méi)有定義,會(huì)使用全局配置,如果此時(shí)你沒(méi)有定義全局配置,則使用默認(rèn)的全局配置100。請(qǐng)結(jié)合實(shí)際業(yè)務(wù)場(chǎng)景進(jìn)行設(shè)置該值。 |
serialPolicy | 未定義 | 指定遠(yuǎn)程緩存VALUE的序列化方式,支持SerialPolicy.JAVA 、SerialPolicy.KRYO 。如果注解上沒(méi)有定義,會(huì)使用全局配置,如果你沒(méi)有定義全局配置,則使用默認(rèn)的全局配置SerialPolicy.JAVA。 |
keyConvertor | 未定義 | 指定KEY的轉(zhuǎn)換方式,用于將復(fù)雜的KEY類型轉(zhuǎn)換為緩存實(shí)現(xiàn)可以接受的類型,支持:KeyConvertor.FASTJSON 、KeyConvertor.NONE 。NONE表示不轉(zhuǎn)換,F(xiàn)ASTJSON可以將復(fù)雜對(duì)象KEY轉(zhuǎn)換成String。如果注解上沒(méi)有定義,會(huì)使用全局配置。 |
key | 未定義 | 使用SpEL指定緩存key,如果沒(méi)有指定會(huì)根據(jù)入?yún)⒆詣?dòng)生成。 |
cacheNullValue | false | 當(dāng)方法返回值為null的時(shí)候是否要緩存。 |
condition | 未定義 | 使用SpEL指定條件,如果表達(dá)式返回true的時(shí)候才去緩存中查詢。 |
postCondition | 未定義 | 使用SpEL指定條件,如果表達(dá)式返回true的時(shí)候才更新緩存,該評(píng)估在方法執(zhí)行后進(jìn)行,因此可以訪問(wèn)到#result。 |
@CacheInvalidate
用于移除緩存,配置說(shuō)明:
配置 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|
area | "default" | 如果在配置中配置了多個(gè)緩存area,在這里指定使用哪個(gè)area。 |
name | 無(wú) | 指定緩存的唯一名稱,一般指向?qū)?yīng)的@Cached定義的name。 |
key | 未定義 | 使用SpEL指定key,如果沒(méi)有指定會(huì)根據(jù)入?yún)⒆詣?dòng)生成。 |
condition | 未定義 | 使用SpEL指定條件,如果表達(dá)式返回true才執(zhí)行刪除,可訪問(wèn)方法結(jié)果#result。刪除緩存實(shí)例中key的元素。 |
multi | false | 如果根據(jù)SpEL指定的key是一個(gè)集合,是否從緩存實(shí)例中刪除對(duì)應(yīng)的每個(gè)緩存。如果設(shè)置為true,但是key不是集合,則不會(huì)刪除緩存。 |
@CacheUpdate
用于更新緩存,配置說(shuō)明:
配置 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|
area | "default" | 如果在配置中配置了多個(gè)緩存area,在這里指定使用哪個(gè)area。 |
name | 無(wú) | 指定緩存的唯一名稱,一般指向?qū)?yīng)的@Cached定義的name。 |
key | 未定義 | 使用SpEL指定key,如果沒(méi)有指定會(huì)根據(jù)入?yún)⒆詣?dòng)生成。 |
value | 無(wú) | 使用SpEL指定value。 |
condition | 未定義 | 使用SpEL指定條件,如果表達(dá)式返回true才執(zhí)行更新,可訪問(wèn)方法結(jié)果#result。更新緩存實(shí)例中key的元素。 |
multi | false | 如果根據(jù)SpEL指定key和value都是集合并且元素的個(gè)數(shù)相同,則是否更新緩存實(shí)例中的對(duì)應(yīng)的每個(gè)元素。如果設(shè)置為true,但是key不是集合或者value不是集合或者它們的元素的個(gè)數(shù)不相同,也不會(huì)更新緩存。 |
@CacheRefresh
用于自定刷新緩存,配置說(shuō)明:
配置 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|
refresh | 無(wú) | 刷新間隔 |
stopRefreshAfterLastAccess | 未定義 | 指定該key多長(zhǎng)時(shí)間沒(méi)有訪問(wèn)就停止刷新,如果不指定會(huì)一直刷新。 |
refreshLockTimeout | 60秒 | 類型為BOTH/REMOTE的緩存刷新時(shí),同時(shí)只會(huì)有一臺(tái)服務(wù)器在刷新,這臺(tái)服務(wù)器會(huì)在遠(yuǎn)程緩存放置一個(gè)分布式鎖,此配置指定該鎖的超時(shí)時(shí)間。 |
timeUnit | TimeUnit.SECONDS | 指定refresh時(shí)間單位。 |
@CachePenetrationProtect
當(dāng)緩存訪問(wèn)未命中的情況下,對(duì)并發(fā)進(jìn)行的加載行為進(jìn)行保護(hù)。 當(dāng)前版本實(shí)現(xiàn)的是單JVM內(nèi)的保護(hù),即同一個(gè)JVM中同一個(gè)key只有一個(gè)線程去加載,其它線程等待結(jié)果,配置說(shuō)明:
配置 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|
value | true | 是否開啟保護(hù)模式。 |
timeout | 未定義 | 其他線程的等待超時(shí)時(shí)間,如果超時(shí)則自己執(zhí)行方法直接返回結(jié)果。 |
timeUnit | TimeUnit.SECONDS | 指定timeout時(shí)間單位。 |
@CreateCache
在Spring Bean中使用該注解可創(chuàng)建一個(gè)Cache實(shí)例,配置說(shuō)明:
配置 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|
area | "default" | 如果在配置中配置了多個(gè)緩存area,在這里指定使用哪個(gè)area。 |
name | 未定義 | 指定緩存實(shí)例名稱,如果沒(méi)有指定,會(huì)根據(jù)類名+方法名自動(dòng)生成。name會(huì)被用于遠(yuǎn)程緩存的key前綴。另外在統(tǒng)計(jì)中,一個(gè)簡(jiǎn)短有意義的名字會(huì)提高可讀性。 |
timeUnit | TimeUnit.SECONDS | 指定expire的單位。 |
expire | 未定義 | 超時(shí)時(shí)間。如果注解上沒(méi)有定義,會(huì)使用全局配置,如果此時(shí)全局配置也沒(méi)有定義,則為無(wú)窮大。 |
localExpire | 未定義 | 僅當(dāng)cacheType為BOTH時(shí)適用,為本地緩存指定一個(gè)不一樣的超時(shí)時(shí)間,通常應(yīng)該小于expire。如果沒(méi)有設(shè)置localExpire且cacheType為BOTH,那么本地緩存的超時(shí)時(shí)間和遠(yuǎn)程緩存保持一致。 |
cacheType | CacheType.REMOTE | 緩存的類型,支持:REMOTE 、LOCAL 、BOTH ,如果定義為BOTH,會(huì)使用LOCAL和REMOTE組合成兩級(jí)緩存。 |
localLimit | 未定義 | 如果cacheType為L(zhǎng)OCAL或BOTH,這個(gè)參數(shù)指定本地緩存的最大元素?cái)?shù)量,以控制內(nèi)存占用。如果注解上沒(méi)有定義,會(huì)使用全局配置,如果此時(shí)你沒(méi)有定義全局配置,則使用默認(rèn)的全局配置100。請(qǐng)結(jié)合實(shí)際業(yè)務(wù)場(chǎng)景進(jìn)行設(shè)置該值。 |
serialPolicy | 未定義 | 指定遠(yuǎn)程緩存VALUE的序列化方式,支持SerialPolicy.JAVA 、SerialPolicy.KRYO 。如果注解上沒(méi)有定義,會(huì)使用全局配置,如果你沒(méi)有定義全局配置,則使用默認(rèn)的全局配置SerialPolicy.JAVA。 |
keyConvertor | 未定義 | 指定KEY的轉(zhuǎn)換方式,用于將復(fù)雜的KEY類型轉(zhuǎn)換為緩存實(shí)現(xiàn)可以接受的類型,支持:KeyConvertor.FASTJSON 、KeyConvertor.NONE 。NONE表示不轉(zhuǎn)換,F(xiàn)ASTJSON可以將復(fù)雜對(duì)象KEY轉(zhuǎn)換成String。如果注解上沒(méi)有定義,會(huì)使用全局配置。 |
使用示例
/** * 啟動(dòng)類 */ @SpringBootApplication @EnableCreateCacheAnnotation @EnableMethodCache(basePackages = "com.xxx.xxx") public class Application { public static void main(String[] args){ SpringApplication.run(Application.class, args); } } /** * 接口 */ public interface JetCacheExampleService { User getValue(long userId); void updateValue(User user); void deleteValue(User user); } /** * 實(shí)現(xiàn)類 */ @Service public class JetCacheExampleServiceImpl implements JetCacheExampleService { @CreateCache(name = "JetCacheExampleServiceImpl.exampleCache" , localLimit = 50 ,cacheType = CacheType.LOCAL) @CachePenetrationProtect private Cache<Long, User> exampleCache; @Override @Cached(name = "JetCacheExampleService.getValue", expire = 3600 * 6, localLimit = 50, cacheType = CacheType.BOTH) @CacheRefresh(refresh = 3600, stopRefreshAfterLastAccess = 3600 * 2) @CachePenetrationProtect public User getValue(long userId){ String result = new User(); // ... 處理邏輯 return result; } @Override @CacheUpdate(name = "JetCacheExampleService.getValue", key="#user.userId", value="#user") public void updateValue(User user){ // 處理邏輯 } @Override @CacheInvalidate(name = "JetCacheExampleService.getValue", key="#user.userId") public void deleteValue(User user){ // 處理邏輯 } }
如上述所示
getValue方法會(huì)創(chuàng)建一個(gè)緩存實(shí)例,通過(guò)@Cached
注解可以看到緩存實(shí)例名稱cacheName
為'JetCacheExampleService.getValue',緩存的有效時(shí)長(zhǎng)為6小時(shí),本地緩存的數(shù)量最多為50,緩存類型為BOTH
(優(yōu)先從本地緩存獲取);通過(guò)@CacheRefresh
注解可以看到會(huì)為該緩存實(shí)例設(shè)置一個(gè)刷新策略,刷新間隔為1小時(shí),2個(gè)小時(shí)沒(méi)訪問(wèn)后不再刷新,需要刷新的緩存實(shí)例會(huì)為其每一個(gè)緩存數(shù)據(jù)創(chuàng)建一個(gè)RefreshTask
周期性任務(wù);@CachePenetrationProtect
注解表示該緩存實(shí)例開啟保護(hù)模式,當(dāng)緩存未命中,同一個(gè)JVM中同一個(gè)key只有一個(gè)線程去加載數(shù)據(jù),其它線程等待結(jié)果。
updateValue方法可以更新緩存,通過(guò)@CacheUpdate
注解可以看到會(huì)更新緩存實(shí)例'JetCacheExampleService.getValue'中緩存key為#user.userId的緩存value為#user。
deleteValue方法可以刪除緩存,通過(guò)@CacheInvalidate
注解可以看到會(huì)刪除緩存實(shí)例'JetCacheExampleService.getValue'中緩存key為#user.userId緩存數(shù)據(jù)。
exampleCache字段會(huì)作為一個(gè)緩存實(shí)例對(duì)象,通過(guò)@CreateCache
注解可以看到,會(huì)將該字段作為cacheName
為'JetCacheExampleService.getValue'緩存實(shí)例對(duì)象,本地緩存的數(shù)量最多為50,緩存類型為LOCAL
,@CachePenetrationProtect
注解表示該緩存實(shí)例開啟保護(hù)模式。
我的業(yè)務(wù)場(chǎng)景是使用上述的getValue方法創(chuàng)建緩存實(shí)例即可。
注意:
@Cached
注解不能和@CacheUpdate
或者@CacheInvalidate
同時(shí)使用@CacheInvalidate
可以多個(gè)同時(shí)使用
另外通過(guò)@CreateCache注解創(chuàng)建緩存實(shí)例也可以這樣初始化:
@Service public class JetCacheExampleServiceImpl implements JetCacheExampleService { @CreateCache(name = "JetCacheExampleServiceImpl.exampleCache" , localLimit = 50 ,cacheType = CacheType.LOCAL) private Cache<Long, User> exampleCache; @PostConstruct public exampleCacheInit(){ RefreshPolicy policy = RefreshPolicy.newPolicy(60, TimeUnit.MINUTES) .stopRefreshAfterLastAccess(120, TimeUnit.MINUTES); exampleCache.config().setLoader(this::loadFromDatabase); exampleCache.config().setRefreshPolicy(policy); } }
更加詳細(xì)的使用方法請(qǐng)參考JetCache
官方地址。
三、源碼解析
參考本人Git倉(cāng)庫(kù)中的JetCache
項(xiàng)目,已做詳細(xì)的注釋。
簡(jiǎn)單概括:利用Spring AOP功能,在調(diào)用需要緩存的方法前,通過(guò)解析注解獲取緩存配置,根據(jù)這些配置創(chuàng)建不同的實(shí)例對(duì)象,進(jìn)行緩存等操作。
JetCache
分為兩部分,一部分是Cache API以及實(shí)現(xiàn),另一部分是注解支持。
項(xiàng)目的各個(gè)子模塊
- jetcache-anno-api:定義
JetCache
注解和常量。 - jetcache-core:核心API,Cache接口的實(shí)現(xiàn),提供各種緩存實(shí)例的操作,不依賴于Spring。
- jetcache-autoconfigure:完成初始化,解析application.yml配置文件中的相關(guān)配置,以提供不同緩存實(shí)例的
CacheBuilder
構(gòu)造器 - jetcache-anno:基于Spring提供
@Cached
和@CreateCache
注解支持,初始化Spring AOP以及JetCache
注解等配置。 - jetcache-redis:使用Jedis提供Redis支持。
- jetcache-redis-lettuce:使用Lettuce提供Redis支持,實(shí)現(xiàn)了
JetCache
異步訪問(wèn)緩存的的接口。 - jetcache-redis-springdata:使用Spring Data提供Redis支持。
- jetcache-starter-redis:提供pom文件,Spring Boot方式的Starter,基于Jedis。
- jetcache-starter-redis-lettuce:提供pom文件,Spring Boot方式的Starter,基于Lettuce。
- jetcache-starter-redis-springdata:提供pom文件,Spring Boot方式的Starter,基于Spring Data。
- jetcache-test:提供相關(guān)測(cè)試。
常用注解與變量
在jetcache-anno-api模塊中定義了需要用的緩存注解與常量,在上述已經(jīng)詳細(xì)的講述過(guò),其中@CacheInvalidateContainer
注解定義value為@CacheInvalidate
數(shù)組,然后通過(guò)jdk8新增的@Repeatable
注解,在@CacheInvalidate
注解上面添加@Repeatable(CacheInvalidateContainer.class)
,即可支持同一個(gè)地方可以使用多個(gè)@CacheInvalidate
注解。
緩存API
主要查看jetcache-core子模塊,提供各種Cache
緩存,以支持不同的緩存類型
Cache接口的子關(guān)系,結(jié)構(gòu)如下圖:
主要對(duì)象描述:
- Cache:緩存接口,定義基本方法
- AbstractCache:抽象類,緩存接口的繼承者,提供基本實(shí)現(xiàn),具體實(shí)現(xiàn)交由不同的子類
- LinkedHashMapCache:基于LinkedHashMap設(shè)計(jì)的簡(jiǎn)易內(nèi)存緩存
- CaffeineCache:基于Caffeine工具設(shè)計(jì)的內(nèi)存緩存
- RedisCache:Redis實(shí)現(xiàn),使用Jedis客戶端
- RedisLettuceCache:Redis實(shí)現(xiàn),使用Lettuce客戶端
- MultiLevelCache:兩級(jí)緩存,用于封裝EmbeddedCache(本地緩存)和ExternalCache(遠(yuǎn)程緩存)
- RefreshCache:基于裝飾器模式Decorator,提供自動(dòng)刷新功能
- LazyInitCache:用于@CreateCache注解創(chuàng)建的緩存實(shí)例,依賴于Spring
Cache接口
com.alicp.jetcache.Cache
接口,定義了緩存實(shí)例的操作方法(部分有默認(rèn)實(shí)現(xiàn)),以及獲取分布式鎖(非嚴(yán)格,用于刷新遠(yuǎn)程緩存)的實(shí)現(xiàn),因?yàn)槔^承了java.io.Closeable
接口,所以也提供了close方法的默認(rèn)實(shí)現(xiàn),空方法,交由不同緩存實(shí)例的實(shí)現(xiàn)去實(shí)現(xiàn)該方法用于釋放資源,在com.alicp.jetcache.anno.support.ConfigProvider.doShutdown()
方法中會(huì)調(diào)用每個(gè)緩存實(shí)例對(duì)象的close方法進(jìn)行資源釋放。主要代碼如下:
public interface Cache<K, V> extends Closeable { Logger logger = LoggerFactory.getLogger(Cache.class); //-----------------------------JSR 107 style API------------------------------------------------ default V get(K key) throws CacheInvokeException { CacheGetResult<V> result = GET(key); if (result.isSuccess()) { return result.getValue(); } else { return null; } } default Map<K, V> getAll(Set<? extends K> keys) throws CacheInvokeException { MultiGetResult<K, V> cacheGetResults = GET_ALL(keys); return cacheGetResults.unwrapValues(); } default void put(K key, V value) { PUT(key, value); } default void putAll(Map<? extends K, ? extends V> map) { PUT_ALL(map); } default boolean putIfAbsent(K key, V value) { // 多級(jí)緩存MultiLevelCache不支持此方法 CacheResult result = PUT_IF_ABSENT(key, value, config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS); return result.getResultCode() == CacheResultCode.SUCCESS; } default boolean remove(K key) { return REMOVE(key).isSuccess(); } default void removeAll(Set<? extends K> keys) { REMOVE_ALL(keys); } <T> T unwrap(Class<T> clazz); @Override default void close() { } //--------------------------JetCache API--------------------------------------------- CacheConfig<K, V> config(); default AutoReleaseLock tryLock(K key, long expire, TimeUnit timeUnit) { if (key == null) { return null; } // 隨機(jī)生成一個(gè)值 final String uuid = UUID.randomUUID().toString(); // 過(guò)期時(shí)間 final long expireTimestamp = System.currentTimeMillis() + timeUnit.toMillis(expire); final CacheConfig config = config(); AutoReleaseLock lock = () -> { // 創(chuàng)建一把會(huì)自動(dòng)釋放資源的鎖,實(shí)現(xiàn)其 close() 方法 int unlockCount = 0; while (unlockCount++ < config.getTryLockUnlockCount()) { if(System.currentTimeMillis() < expireTimestamp) { // 這把鎖還沒(méi)有過(guò)期,則刪除 // 刪除對(duì)應(yīng)的 Key 值 // 出現(xiàn)的結(jié)果:成功,失敗,Key 不存在 CacheResult unlockResult = REMOVE(key); if (unlockResult.getResultCode() == CacheResultCode.FAIL || unlockResult.getResultCode() == CacheResultCode.PART_SUCCESS) { // 刪除對(duì)應(yīng)的 Key 值過(guò)程中出現(xiàn)了異常,則重試 logger.info("[tryLock] [{} of {}] [{}] unlock failed. Key={}, msg = {}", unlockCount, config.getTryLockUnlockCount(), uuid, key, unlockResult.getMessage()); // retry } else if (unlockResult.isSuccess()) { // 釋放成功 logger.debug("[tryLock] [{} of {}] [{}] successfully release the lock. Key={}", unlockCount, config.getTryLockUnlockCount(), uuid, key); return; } else { // 鎖已經(jīng)被釋放了 logger.warn("[tryLock] [{} of {}] [{}] unexpected unlock result: Key={}, result={}", unlockCount, config.getTryLockUnlockCount(), uuid, key, unlockResult.getResultCode()); return; } } else { // 該鎖已失效 logger.info("[tryLock] [{} of {}] [{}] lock already expired: Key={}", unlockCount, config.getTryLockUnlockCount(), uuid, key); return; } } }; int lockCount = 0; Cache cache = this; while (lockCount++ < config.getTryLockLockCount()) { // 往 Redis(或者本地) 中存放 Key 值(_#RL#結(jié)尾的Key) // 返回的結(jié)果:成功、已存在、失敗 CacheResult lockResult = cache.PUT_IF_ABSENT(key, uuid, expire, timeUnit); if (lockResult.isSuccess()) { // 成功獲取到鎖 logger.debug("[tryLock] [{} of {}] [{}] successfully get a lock. Key={}", lockCount, config.getTryLockLockCount(), uuid, key); return lock; } else if (lockResult.getResultCode() == CacheResultCode.FAIL || lockResult.getResultCode() == CacheResultCode.PART_SUCCESS) { logger.info("[tryLock] [{} of {}] [{}] cache access failed during get lock, will inquiry {} times. Key={}, msg={}", lockCount, config.getTryLockLockCount(), uuid, config.getTryLockInquiryCount(), key, lockResult.getMessage()); // 嘗試獲取鎖的過(guò)程中失敗了,也就是往 Redis 中存放 Key 值出現(xiàn)異常 // 這個(gè)時(shí)候可能 Key 值已經(jīng)存儲(chǔ)了,但是由于其他原因?qū)е路祷氐慕Y(jié)果表示執(zhí)行失敗 int inquiryCount = 0; while (inquiryCount++ < config.getTryLockInquiryCount()) { CacheGetResult inquiryResult = cache.GET(key); if (inquiryResult.isSuccess()) { if (uuid.equals(inquiryResult.getValue())) { logger.debug("[tryLock] [{} of {}] [{}] successfully get a lock after inquiry. Key={}", inquiryCount, config.getTryLockInquiryCount(), uuid, key); return lock; } else { logger.debug("[tryLock] [{} of {}] [{}] not the owner of the lock, return null. Key={}", inquiryCount, config.getTryLockInquiryCount(), uuid, key); return null; } } else { logger.info("[tryLock] [{} of {}] [{}] inquiry failed. Key={}, msg={}", inquiryCount, config.getTryLockInquiryCount(), uuid, key, inquiryResult.getMessage()); // retry inquiry } } } else { // 已存在表示該鎖被其他人占有 // others holds the lock logger.debug("[tryLock] [{} of {}] [{}] others holds the lock, return null. Key={}", lockCount, config.getTryLockLockCount(), uuid, key); return null; } } logger.debug("[tryLock] [{}] return null after {} attempts. Key={}", uuid, config.getTryLockLockCount(), key); return null; } default boolean tryLockAndRun(K key, long expire, TimeUnit timeUnit, Runnable action){ // Release the lock use Java 7 try-with-resources. try (AutoReleaseLock lock = tryLock(key, expire, timeUnit)) { // 嘗試獲取鎖 if (lock != null) { // 獲取到鎖則執(zhí)行下面的任務(wù) action.run(); return true; } else { return false; } // 執(zhí)行完鎖的操作后會(huì)進(jìn)行資源釋放,調(diào)用 AutoCloseable 的 close() 方法 } } CacheGetResult<V> GET(K key); MultiGetResult<K, V> GET_ALL(Set<? extends K> keys); default V computeIfAbsent(K key, Function<K, V> loader) { return computeIfAbsent(key, loader, config().isCacheNullValue()); } V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull); V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull, long expireAfterWrite, TimeUnit timeUnit); default void put(K key, V value, long expireAfterWrite, TimeUnit timeUnit) { PUT(key, value, expireAfterWrite, timeUnit); } default CacheResult PUT(K key, V value) { if (key == null) { return CacheResult.FAIL_ILLEGAL_ARGUMENT; } return PUT(key, value, config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS); } CacheResult PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit); default void putAll(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit) { PUT_ALL(map, expireAfterWrite, timeUnit); } default CacheResult PUT_ALL(Map<? extends K, ? extends V> map) { if (map == null) { return CacheResult.FAIL_ILLEGAL_ARGUMENT; } return PUT_ALL(map, config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS); } CacheResult PUT_ALL(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit); CacheResult REMOVE(K key); CacheResult REMOVE_ALL(Set<? extends K> keys); CacheResult PUT_IF_ABSENT(K key, V value, long expireAfterWrite, TimeUnit timeUnit); }
com.alicp.jetcache.Cache
定義的方法大都是關(guān)于緩存的獲取、刪除和存放操作
其中大寫的方法返回JetCache
自定義的CacheResult(完整的返回值,可以清晰的知道執(zhí)行結(jié)果,例如get返回null的時(shí)候,無(wú)法斷定是對(duì)應(yīng)的key不存在,還是訪問(wèn)緩存發(fā)生了異常)
小寫的方法默認(rèn)實(shí)現(xiàn)就是調(diào)用大寫的方法
computeIfAbsent
方法最為核心,交由子類去實(shí)現(xiàn)
tryLockAndRun
方法會(huì)非堵塞的嘗試獲取一把AutoReleaseLock分布式鎖(非嚴(yán)格),獲取過(guò)程:
- 嘗試往Redis中設(shè)置(已存在無(wú)法設(shè)置)一個(gè)鍵值對(duì),key為緩存
key_#RL#
,value為UUID
,并設(shè)置這個(gè)鍵值對(duì)的過(guò)期時(shí)間為60秒(默認(rèn)) - 如果獲取到鎖后進(jìn)行加載任務(wù),也就是重新加載方法并更新遠(yuǎn)程緩存
- 該鎖實(shí)現(xiàn)了java.lang.AutoCloseable接口,使用try-with-resource方式,在執(zhí)行完加載任務(wù)后會(huì)自動(dòng)釋放資源,也就是調(diào)用close方法將獲取鎖過(guò)程中設(shè)置的鍵值對(duì)從Redis中刪除
- 在RefreshCache中會(huì)調(diào)用該方法,因?yàn)槿绻嬖谶h(yuǎn)程緩存需要刷新則需要采用分布式鎖的方式
AbstractCache抽象類
com.alicp.jetcache.AbstractCache
抽象類,實(shí)現(xiàn)了Cache接口,主要代碼如下:
public abstract class AbstractCache<K, V> implements Cache<K, V> { /** * 當(dāng)緩存未命中時(shí),并發(fā)情況同一個(gè)Key是否只允許一個(gè)線程去加載,其他線程等待結(jié)果(可以設(shè)置timeout,超時(shí)則自己加載并直接返回) * 如果是的話則由獲取到Key對(duì)應(yīng)的 LoaderLock.signal(采用了 CountDownLatch)的線程進(jìn)行加載 * loaderMap臨時(shí)保存 Key 對(duì)應(yīng)的 LoaderLock 對(duì)象 */ private volatile ConcurrentHashMap<Object, LoaderLock> loaderMap; ConcurrentHashMap<Object, LoaderLock> initOrGetLoaderMap() { if (loaderMap == null) { synchronized (this) { if (loaderMap == null) { loaderMap = new ConcurrentHashMap<>(); } } } return loaderMap; } @Override public final V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull) { return computeIfAbsentImpl(key, loader, cacheNullWhenLoaderReturnNull, 0, null, this); } @Override public final V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull, long expireAfterWrite, TimeUnit timeUnit) { return computeIfAbsentImpl(key, loader, cacheNullWhenLoaderReturnNull, expireAfterWrite, timeUnit, this); } private static <K, V> boolean needUpdate(V loadedValue, boolean cacheNullWhenLoaderReturnNull, Function<K, V> loader) { if (loadedValue == null && !cacheNullWhenLoaderReturnNull) { return false; } if (loader instanceof CacheLoader && ((CacheLoader<K, V>) loader).vetoCacheUpdate()) { return false; } return true; } static <K, V> V computeIfAbsentImpl(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull, long expireAfterWrite, TimeUnit timeUnit, Cache<K, V> cache) { // 獲取內(nèi)部的 Cache 對(duì)象 AbstractCache<K, V> abstractCache = CacheUtil.getAbstractCache(cache); // 封裝 loader 函數(shù)成一個(gè) ProxyLoader 對(duì)象,主要在重新加載緩存后發(fā)出一個(gè) CacheLoadEvent 到 CacheMonitor CacheLoader<K, V> newLoader = CacheUtil.createProxyLoader(cache, loader, abstractCache::notify); CacheGetResult<V> r; if (cache instanceof RefreshCache) { // 該緩存實(shí)例需要刷新 RefreshCache<K, V> refreshCache = ((RefreshCache<K, V>) cache); /* * 從緩存中獲取數(shù)據(jù) * 如果是多級(jí)緩存(先從本地緩存獲取,獲取不到則從遠(yuǎn)程緩存獲?。? * 如果緩存數(shù)據(jù)是從遠(yuǎn)程緩存獲取到的數(shù)據(jù)則會(huì)更新至本地緩存,并且如果本地緩存沒(méi)有設(shè)置 localExpire 則使用遠(yuǎn)程緩存的到期時(shí)間作為自己的到期時(shí)間 * 我一般不設(shè)置 localExpire ,因?yàn)榭赡軐?dǎo)致本地緩存的有效時(shí)間比遠(yuǎn)程緩存的有效時(shí)間更長(zhǎng) * 如果設(shè)置 localExpire 了記得設(shè)置 expireAfterAccessInMillis */ r = refreshCache.GET(key); // 添加/更新當(dāng)前 RefreshCache 的刷新緩存任務(wù),存放于 RefreshCache 的 taskMap 中 refreshCache.addOrUpdateRefreshTask(key, newLoader); } else { // 從緩存中獲取數(shù)據(jù) r = cache.GET(key); } if (r.isSuccess()) { // 緩存命中 return r.getValue(); } else { // 緩存未命中 // 創(chuàng)建當(dāng)緩存未命中去更新緩存的函數(shù) Consumer<V> cacheUpdater = (loadedValue) -> { if(needUpdate(loadedValue, cacheNullWhenLoaderReturnNull, newLoader)) { /* * 未在緩存注解中配置 key 的生成方式則默認(rèn)取入?yún)⒆鳛榫彺?key * 在進(jìn)入當(dāng)前方法時(shí)是否可以考慮為 key 創(chuàng)建一個(gè)副本???? * 因?yàn)榫彺嫖疵腥缓笸ㄟ^(guò) loader 重新加載方法時(shí),如果方法內(nèi)部對(duì)入?yún)⑦M(jìn)行了修改,那么生成的緩存 key 也會(huì)被修改 * 從而導(dǎo)致相同的 key 進(jìn)入該方法時(shí)一直與緩存中的 key 不相同,一直出現(xiàn)緩存未命中 */ if (timeUnit != null) { cache.PUT(key, loadedValue, expireAfterWrite, timeUnit).waitForResult(); } else { cache.PUT(key, loadedValue).waitForResult(); } } }; V loadedValue; if (cache.config().isCachePenetrationProtect()) { // 添加了 @CachePenetrationProtect 注解 // 一個(gè)JVM只允許一個(gè)線程執(zhí)行 loadedValue = synchronizedLoad(cache.config(), abstractCache, key, newLoader, cacheUpdater); } else { // 執(zhí)行方法 loadedValue = newLoader.apply(key); // 將新的結(jié)果異步緩存 cacheUpdater.accept(loadedValue); } return loadedValue; } } static <K, V> V synchronizedLoad(CacheConfig config, AbstractCache<K,V> abstractCache, K key, Function<K, V> newLoader, Consumer<V> cacheUpdater) { ConcurrentHashMap<Object, LoaderLock> loaderMap = abstractCache.initOrGetLoaderMap(); Object lockKey = buildLoaderLockKey(abstractCache, key); while (true) { // 為什么加一個(gè) create[] 數(shù)組 疑問(wèn)?? boolean create[] = new boolean[1]; LoaderLock ll = loaderMap.computeIfAbsent(lockKey, (unusedKey) -> { create[0] = true; LoaderLock loaderLock = new LoaderLock(); loaderLock.signal = new CountDownLatch(1); loaderLock.loaderThread = Thread.currentThread(); return loaderLock; }); if (create[0] || ll.loaderThread == Thread.currentThread()) { try { // 加載該 Key 實(shí)例的方法 V loadedValue = newLoader.apply(key); ll.success = true; ll.value = loadedValue; // 將重新加載的數(shù)據(jù)更新至緩存 cacheUpdater.accept(loadedValue); return loadedValue; } finally { // 標(biāo)記已完成 ll.signal.countDown(); if (create[0]) { loaderMap.remove(lockKey); } } } else { // 等待其他線程加載,如果出現(xiàn)異常或者超時(shí)則自己加載返回?cái)?shù)據(jù),但是不更新緩存 try { Duration timeout = config.getPenetrationProtectTimeout(); if (timeout == null) { ll.signal.await(); } else { boolean ok = ll.signal.await(timeout.toMillis(), TimeUnit.MILLISECONDS); if(!ok) { logger.info("loader wait timeout:" + timeout); return newLoader.apply(key); } } } catch (InterruptedException e) { logger.warn("loader wait interrupted"); return newLoader.apply(key); } if (ll.success) { return (V) ll.value; } else { continue; } } } } private static Object buildLoaderLockKey(Cache c, Object key) { if (c instanceof AbstractEmbeddedCache) { return ((AbstractEmbeddedCache) c).buildKey(key); } else if (c instanceof AbstractExternalCache) { byte bytes[] = ((AbstractExternalCache) c).buildKey(key); return ByteBuffer.wrap(bytes); } else if (c instanceof MultiLevelCache) { c = ((MultiLevelCache) c).caches()[0]; return buildLoaderLockKey(c, key); } else if(c instanceof ProxyCache) { c = ((ProxyCache) c).getTargetCache(); return buildLoaderLockKey(c, key); } else { throw new CacheException("impossible"); } } /** * 重新加載數(shù)據(jù)鎖 */ static class LoaderLock { /** * 柵欄 */ CountDownLatch signal; /** * 持有的線程 */ Thread loaderThread; /** * 是否加載成功 */ boolean success; /** * 加載出來(lái)的數(shù)據(jù) */, Object value; } }
com.alicp.jetcache.AbstractCache
實(shí)現(xiàn)了Cache
接口的大寫方法,內(nèi)部調(diào)用自己定義的抽象方法(以DO_
開頭,交由不同的子類實(shí)現(xiàn)),操作緩存后發(fā)送相應(yīng)的事件CacheEvent
,也就是調(diào)用自己定義的notify方法,遍歷每個(gè)CacheMonitor
對(duì)該事件進(jìn)行后置操作,用于統(tǒng)計(jì)信息。
computeIfAbsentImpl
方法實(shí)現(xiàn)了Cache
接口的核心方法,從緩存實(shí)例中根據(jù)緩存key獲取緩存value,邏輯如下:
- 獲取cache的targetCache,因?yàn)槲覀兺ㄟ^(guò)
@CreateCache
注解創(chuàng)建的緩存實(shí)例將生成LazyInitCache
對(duì)象,需要調(diào)用其getTargetCache方法才會(huì)完成緩存實(shí)例的初始化 - loader函數(shù)是對(duì)加載原有方法的封裝,這里再進(jìn)行一層封裝,封裝成
ProxyLoader
類型,目的是在加載原有方法后將發(fā)送CacheLoadEvent
事件 - 從緩存實(shí)例中獲取對(duì)應(yīng)的緩存value,如果緩存實(shí)例對(duì)象是
RefreshCache
類型(在com.alicp.jetcache.anno.support.CacheContext.buildCache
方法中會(huì)將cache包裝成CacheHandlerRefreshCache
),則調(diào)用RefreshCache.addOrUpdateRefreshTask
方法,判斷是否應(yīng)該為它添加一個(gè)定時(shí)的刷新任務(wù) - 如果緩存未命中,則執(zhí)行l(wèi)oader函數(shù),如果開啟了保護(hù)模式,則調(diào)用自定義的synchronizedLoad方法,大致邏輯:根據(jù)緩存key從自己的loaderMap(線程安全)遍歷中嘗試獲?。ú淮嬖趧t創(chuàng)建)
LoaderLock
加載鎖,獲取到這把加載鎖才可以執(zhí)行l(wèi)oader函數(shù),如果已被其他線程占有則進(jìn)行等待(沒(méi)有設(shè)置超時(shí)時(shí)間則一直等待),通過(guò)CountDownLatch
計(jì)數(shù)器實(shí)現(xiàn)
AbstractEmbeddedCache本地緩存
com.alicp.jetcache.embedded.AbstractEmbeddedCache
抽象類繼承AbstractCache抽象類,定義了本地緩存的存放緩存數(shù)據(jù)的對(duì)象為com.alicp.jetcache.embedded.InnerMap
接口和一個(gè)初始化該接口的createAreaCache抽象方法,基于InnerMap接口實(shí)現(xiàn)以DO_
開頭的方法,完成緩存實(shí)例各種操作的具體實(shí)現(xiàn),主要代碼如下:
public abstract class AbstractEmbeddedCache<K, V> extends AbstractCache<K, V> { protected EmbeddedCacheConfig<K, V> config; /** * 本地緩存的 Map */ protected InnerMap innerMap; protected abstract InnerMap createAreaCache(); public AbstractEmbeddedCache(EmbeddedCacheConfig<K, V> config) { this.config = config; innerMap = createAreaCache(); } @Override public CacheConfig<K, V> config() { return config; } public Object buildKey(K key) { Object newKey = key; Function<K, Object> keyConvertor = config.getKeyConvertor(); if (keyConvertor != null) { newKey = keyConvertor.apply(key); } return newKey; } @Override protected CacheGetResult<V> do_GET(K key) { Object newKey = buildKey(key); CacheValueHolder<V> holder = (CacheValueHolder<V>) innerMap.getValue(newKey); return parseHolderResult(holder); } protected CacheGetResult<V> parseHolderResult(CacheValueHolder<V> holder) { long now = System.currentTimeMillis(); if (holder == null) { return CacheGetResult.NOT_EXISTS_WITHOUT_MSG; } else if (now >= holder.getExpireTime()) { return CacheGetResult.EXPIRED_WITHOUT_MSG; } else { synchronized (holder) { long accessTime = holder.getAccessTime(); if (config.isExpireAfterAccess()) { long expireAfterAccess = config.getExpireAfterAccessInMillis(); if (now >= accessTime + expireAfterAccess) { return CacheGetResult.EXPIRED_WITHOUT_MSG; } } // 設(shè)置該緩存數(shù)據(jù)的最后一次訪問(wèn)時(shí)間 holder.setAccessTime(now); } return new CacheGetResult(CacheResultCode.SUCCESS, null, holder); } } @Override protected MultiGetResult<K, V> do_GET_ALL(Set<? extends K> keys) { ArrayList<K> keyList = new ArrayList<K>(keys.size()); ArrayList<Object> newKeyList = new ArrayList<Object>(keys.size()); keys.stream().forEach((k) -> { Object newKey = buildKey(k); keyList.add(k); newKeyList.add(newKey); }); Map<Object, CacheValueHolder<V>> innerResultMap = innerMap.getAllValues(newKeyList); Map<K, CacheGetResult<V>> resultMap = new HashMap<>(); for (int i = 0; i < keyList.size(); i++) { K key = keyList.get(i); Object newKey = newKeyList.get(i); CacheValueHolder<V> holder = innerResultMap.get(newKey); resultMap.put(key, parseHolderResult(holder)); } MultiGetResult<K, V> result = new MultiGetResult<>(CacheResultCode.SUCCESS, null, resultMap); return result; } @Override protected CacheResult do_PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit) { CacheValueHolder<V> cacheObject = new CacheValueHolder(value ,timeUnit.toMillis(expireAfterWrite)); innerMap.putValue(buildKey(key), cacheObject); return CacheResult.SUCCESS_WITHOUT_MSG; } @Override protected CacheResult do_PUT_ALL(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit) { HashMap newKeyMap = new HashMap(); for (Map.Entry<? extends K, ? extends V> en : map.entrySet()) { CacheValueHolder<V> cacheObject = new CacheValueHolder(en.getValue(), timeUnit.toMillis(expireAfterWrite)); newKeyMap.put(buildKey(en.getKey()), cacheObject); } innerMap.putAllValues(newKeyMap); final HashMap resultMap = new HashMap(); map.keySet().forEach((k) -> resultMap.put(k, CacheResultCode.SUCCESS)); return CacheResult.SUCCESS_WITHOUT_MSG; } @Override protected CacheResult do_REMOVE(K key) { innerMap.removeValue(buildKey(key)); return CacheResult.SUCCESS_WITHOUT_MSG; } @Override protected CacheResult do_REMOVE_ALL(Set<? extends K> keys) { Set newKeys = keys.stream().map((key) -> buildKey(key)).collect(Collectors.toSet()); innerMap.removeAllValues(newKeys); final HashMap resultMap = new HashMap(); keys.forEach((k) -> resultMap.put(k, CacheResultCode.SUCCESS)); return CacheResult.SUCCESS_WITHOUT_MSG; } @Override protected CacheResult do_PUT_IF_ABSENT(K key, V value, long expireAfterWrite, TimeUnit timeUnit) { CacheValueHolder<V> cacheObject = new CacheValueHolder(value, timeUnit.toMillis(expireAfterWrite)); if (innerMap.putIfAbsentValue(buildKey(key), cacheObject)) { return CacheResult.SUCCESS_WITHOUT_MSG; } else { return CacheResult.EXISTS_WITHOUT_MSG; } } }
com.alicp.jetcache.embedded.AbstractEmbeddedCache
抽象類實(shí)現(xiàn)了操作本地緩存的相關(guān)方法
- 定義了緩存實(shí)例對(duì)象本地緩存的配置信息
EmbeddedCacheConfig
對(duì)象 - 定義了緩存實(shí)例對(duì)象本地緩存基于內(nèi)存操作緩存數(shù)據(jù)的
InnerMap
對(duì)象,它的初始化過(guò)程交由不同的內(nèi)存緩存實(shí)例(LinkedHashMapCache和CaffeineCache)
LinkedHashMapCache
com.alicp.jetcache.embedded.LinkedHashMapCache
基于LinkedHashMap完成緩存實(shí)例對(duì)象本地緩存基于內(nèi)存操作緩存數(shù)據(jù)的InnerMap
對(duì)象的初始化工作,主要代碼如下:
public class LinkedHashMapCache<K, V> extends AbstractEmbeddedCache<K, V> { private static Logger logger = LoggerFactory.getLogger(LinkedHashMapCache.class); public LinkedHashMapCache(EmbeddedCacheConfig<K, V> config) { super(config); // 將緩存實(shí)例添加至 Cleaner addToCleaner(); } protected void addToCleaner() { Cleaner.add(this); } @Override protected InnerMap createAreaCache() { return new LRUMap(config.getLimit(), this); } public void cleanExpiredEntry() { ((LRUMap) innerMap).cleanExpiredEntry(); } /** * 用于本地緩存類型為 linkedhashmap 緩存實(shí)例存儲(chǔ)緩存數(shù)據(jù) */ final class LRUMap extends LinkedHashMap implements InnerMap { /** * 允許的最大緩存數(shù)量 */ private final int max; /** * 緩存實(shí)例鎖 */ private Object lock; public LRUMap(int max, Object lock) { super((int) (max * 1.4f), 0.75f, true); this.max = max; this.lock = lock; } /** * 當(dāng)元素大于最大值時(shí)移除最老的元素 * * @param eldest 最老的元素 * @return 是否刪除 */ @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > max; } /** * 清理過(guò)期的元素 */ void cleanExpiredEntry() { synchronized (lock) { // 占有當(dāng)前緩存實(shí)例這把鎖 for (Iterator it = entrySet().iterator(); it.hasNext();) { Map.Entry en = (Map.Entry) it.next(); Object value = en.getValue(); if (value != null && value instanceof CacheValueHolder) { CacheValueHolder h = (CacheValueHolder) value; /* * 緩存的數(shù)據(jù)已經(jīng)失效了則刪除 * 為什么不對(duì) expireAfterAccess 進(jìn)行判斷,取最小值,疑問(wèn)???? */ if (System.currentTimeMillis() >= h.getExpireTime()) { it.remove(); } } else { // assert false if (value == null) { logger.error("key " + en.getKey() + " is null"); } else { logger.error("value of key " + en.getKey() + " is not a CacheValueHolder. type=" + value.getClass()); } } } } } @Override public Object getValue(Object key) { synchronized (lock) { return get(key); } } @Override public Map getAllValues(Collection keys) { Map values = new HashMap(); synchronized (lock) { for (Object key : keys) { Object v = get(key); if (v != null) { values.put(key, v); } } } return values; } @Override public void putValue(Object key, Object value) { synchronized (lock) { put(key, value); } } @Override public void putAllValues(Map map) { synchronized (lock) { Set<Map.Entry> set = map.entrySet(); for (Map.Entry en : set) { put(en.getKey(), en.getValue()); } } } @Override public boolean removeValue(Object key) { synchronized (lock) { return remove(key) != null; } } @Override public void removeAllValues(Collection keys) { synchronized (lock) { for (Object k : keys) { remove(k); } } } @Override @SuppressWarnings("unchecked") public boolean putIfAbsentValue(Object key, Object value) { /* * 如果緩存 key 不存在,或者對(duì)應(yīng)的 value 已經(jīng)失效則放入,否則返回 false */ synchronized (lock) { CacheValueHolder h = (CacheValueHolder) get(key); if (h == null || parseHolderResult(h).getResultCode() == CacheResultCode.EXPIRED) { put(key, value); return true; } else { return false; } } } } }
com.alicp.jetcache.embedded.LinkedHashMapCache
自定義LRUMap
繼承LinkedHashMap并實(shí)現(xiàn)InnerMap接口
- 自定義
max
字段,存儲(chǔ)元素個(gè)數(shù)的最大值,并設(shè)置初始容量為(max * 1.4f) - 自定義
lock
字段,每個(gè)緩存實(shí)例的鎖,通過(guò)synchronized關(guān)鍵詞保證線程安全,所以性能相對(duì)來(lái)說(shuō)不好 - 覆蓋LinkedHashMap的
removeEldestEntry
方法,當(dāng)元素大于最大值時(shí)移除最老的元素 - 自定義
cleanExpiredEntry
方法,遍歷Map,根據(jù)緩存value(被封裝成的com.alicp.jetcache.CacheValueHolder
對(duì)象,包含緩存數(shù)據(jù)、失效時(shí)間戳和第一次訪問(wèn)的時(shí)間),清理過(guò)期的元素 - 該對(duì)象初始化時(shí)會(huì)被添加至
com.alicp.jetcache.embedded.Cleaner
清理器中,Cleaner會(huì)周期性(每隔60秒)遍歷LinkedHashMapCache緩存實(shí)例,調(diào)用其cleanExpiredEntry方法
Cleaner清理器
com.alicp.jetcache.embedded.Cleaner
用于清理緩存類型為L(zhǎng)inkedHashMapCache的緩存數(shù)據(jù),請(qǐng)查看相應(yīng)注釋,代碼如下:
/** * 執(zhí)行任務(wù):定時(shí)清理(每分鐘) LinkedHashMapCache 緩存實(shí)例中過(guò)期的緩存數(shù)據(jù) */ class Cleaner { /** * 存放弱引用對(duì)象,以防內(nèi)存溢出 * 如果被弱引用的對(duì)象只被當(dāng)前弱引用對(duì)象關(guān)聯(lián)時(shí),gc 時(shí)被弱引用的對(duì)象則會(huì)被回收(取決于被弱引用的對(duì)象是否還與其他強(qiáng)引用對(duì)象關(guān)聯(lián)) * * 個(gè)人理解:當(dāng)某個(gè) LinkedHashMapCache 強(qiáng)引用對(duì)象沒(méi)有被其他對(duì)象(除了這里)引用時(shí),我們應(yīng)該讓這個(gè)對(duì)象被回收, * 但是由于這里使用的也是強(qiáng)引用,這個(gè)對(duì)象被其他強(qiáng)引用對(duì)象關(guān)聯(lián)了,不可能被回收,存在內(nèi)存溢出的危險(xiǎn), * 所以這里使用了弱引用對(duì)象,如果被弱引用的對(duì)象沒(méi)有被其他對(duì)象(除了這里)引用時(shí),這個(gè)對(duì)象會(huì)被回收 * * 舉個(gè)例子:如果我們往一個(gè) Map<Object, Object> 中存放一個(gè)key-value鍵值對(duì) * 假設(shè)對(duì)應(yīng)的鍵已經(jīng)不再使用被回收了,那我們無(wú)法再獲取到對(duì)應(yīng)的值,也無(wú)法被回收,占有一定的內(nèi)存,存在風(fēng)險(xiǎn) */ static LinkedList<WeakReference<LinkedHashMapCache>> linkedHashMapCaches = new LinkedList<>(); static { // 創(chuàng)建一個(gè)線程池,1個(gè)核心線程 ScheduledExecutorService executorService = JetCacheExecutor.defaultExecutor(); // 起一個(gè)循環(huán)任務(wù)一直清理 linkedHashMapCaches 過(guò)期的數(shù)據(jù)(每隔60秒) executorService.scheduleWithFixedDelay(() -> run(), 60, 60, TimeUnit.SECONDS); } static void add(LinkedHashMapCache cache) { synchronized (linkedHashMapCaches) { // 創(chuàng)建一個(gè)弱引用對(duì)象,并添加到清理對(duì)象中 linkedHashMapCaches.add(new WeakReference<>(cache)); } } static void run() { synchronized (linkedHashMapCaches) { Iterator<WeakReference<LinkedHashMapCache>> it = linkedHashMapCaches.iterator(); while (it.hasNext()) { WeakReference<LinkedHashMapCache> ref = it.next(); // 獲取被弱引用的對(duì)象(強(qiáng)引用) LinkedHashMapCache c = ref.get(); if (c == null) { // 表示被弱引用的對(duì)象被標(biāo)記成了垃圾,則移除 it.remove(); } else { c.cleanExpiredEntry(); } } } } }
CaffeineCache
com.alicp.jetcache.embedded.CaffeineCache
基于Caffeine完成緩存實(shí)例對(duì)象本地緩存基于內(nèi)存操作緩存數(shù)據(jù)的InnerMap
對(duì)象的初始化工作,主要代碼如下:
public class CaffeineCache<K, V> extends AbstractEmbeddedCache<K, V> { /** * 緩存實(shí)例對(duì)象 */ private com.github.benmanes.caffeine.cache.Cache cache; public CaffeineCache(EmbeddedCacheConfig<K, V> config) { super(config); } /** * 初始化本地緩存的容器 * * @return Map對(duì)象 */ @Override @SuppressWarnings("unchecked") protected InnerMap createAreaCache() { Caffeine<Object, Object> builder = Caffeine.newBuilder(); // 設(shè)置緩存實(shí)例的最大緩存數(shù)量 builder.maximumSize(config.getLimit()); final boolean isExpireAfterAccess = config.isExpireAfterAccess(); final long expireAfterAccess = config.getExpireAfterAccessInMillis(); // 設(shè)置緩存實(shí)例的緩存數(shù)據(jù)的失效策略 builder.expireAfter(new Expiry<Object, CacheValueHolder>() { /** * 獲取緩存的有效時(shí)間 * * @param value 緩存數(shù)據(jù) * @return 有效時(shí)間 */ private long getRestTimeInNanos(CacheValueHolder value) { long now = System.currentTimeMillis(); long ttl = value.getExpireTime() - now; /* * 如果本地緩存設(shè)置了多長(zhǎng)時(shí)間沒(méi)訪問(wèn)緩存則失效 */ if(isExpireAfterAccess){ // 設(shè)置緩存的失效時(shí)間 // 多長(zhǎng)時(shí)間沒(méi)訪問(wèn)緩存則失效 and 緩存的有效時(shí)長(zhǎng)取 min ttl = Math.min(ttl, expireAfterAccess); } return TimeUnit.MILLISECONDS.toNanos(ttl); } @Override public long expireAfterCreate(Object key, CacheValueHolder value, long currentTime) { return getRestTimeInNanos(value); } @Override public long expireAfterUpdate(Object key, CacheValueHolder value, long currentTime, long currentDuration) { return currentDuration; } @Override public long expireAfterRead(Object key, CacheValueHolder value, long currentTime, long currentDuration) { return getRestTimeInNanos(value); } }); // 構(gòu)建 Cache 緩存實(shí)例 cache = builder.build(); return new InnerMap() { @Override public Object getValue(Object key) { return cache.getIfPresent(key); } @Override public Map getAllValues(Collection keys) { return cache.getAllPresent(keys); } @Override public void putValue(Object key, Object value) { cache.put(key, value); } @Override public void putAllValues(Map map) { cache.putAll(map); } @Override public boolean removeValue(Object key) { return cache.asMap().remove(key) != null; } @Override public void removeAllValues(Collection keys) { cache.invalidateAll(keys); } @Override public boolean putIfAbsentValue(Object key, Object value) { return cache.asMap().putIfAbsent(key, value) == null; } }; } }
com.alicp.jetcache.embedded.CaffeineCache
通過(guò)Caffeine構(gòu)建一個(gè)com.github.benmanes.caffeine.cache.Cache
緩存對(duì)象,然后實(shí)現(xiàn)InnerMap接口,調(diào)用這個(gè)緩存對(duì)象的相關(guān)方法
- 構(gòu)建時(shí)設(shè)置每個(gè)元素的過(guò)期時(shí)間,也就是根據(jù)每個(gè)元素(
com.alicp.jetcache.CacheValueHolder
)的失效時(shí)間戳來(lái)設(shè)置,底層如何實(shí)現(xiàn)的可以參考Caffeine官方地址 - 調(diào)用
com.github.benmanes.caffeine.cache.Cache
的put方法我有遇到過(guò)'unable to create native thread'內(nèi)存溢出的問(wèn)題,所以請(qǐng)結(jié)合實(shí)際業(yè)務(wù)場(chǎng)景合理的設(shè)置緩存相關(guān)配置
AbstractExternalCache遠(yuǎn)程緩存
com.alicp.jetcache.embedded.AbstractExternalCache
抽象類繼承AbstractCache抽象類,定義了緩存實(shí)例對(duì)象遠(yuǎn)程緩存的配置信息ExternalCacheConfig
對(duì)象,提供了將緩存key轉(zhuǎn)換成字節(jié)數(shù)組的方法,代碼比較簡(jiǎn)單。
RedisCache
com.alicp.jetcache.redis.RedisCache
使用Jedis連接Redis,對(duì)遠(yuǎn)程的緩存數(shù)據(jù)進(jìn)行操作,代碼沒(méi)有很復(fù)雜,可查看我的注釋
- 定義了
com.alicp.jetcache.redis.RedisCacheConfig
配置對(duì)象,包含Redis連接池的相關(guān)信息 - 實(shí)現(xiàn)了以
DO_
開頭的方法,也就是通過(guò)Jedis操作緩存數(shù)據(jù)
RedisLettuceCache
com.alicp.jetcache.redis.lettuce.RedisLettuceCache
使用Lettuce連接Redis,對(duì)遠(yuǎn)程的緩存數(shù)據(jù)進(jìn)行操作,代碼沒(méi)有很復(fù)雜,可查看我的注釋
- 定義了
com.alicp.jetcache.redis.lettuce.RedisLettuceCacheConfig
配置對(duì)象,包含Redis客戶端、與Redis建立的安全連接等信息,因?yàn)榈讓邮腔?a rel="external nofollow" target="_blank">Netty實(shí)現(xiàn)的,所以無(wú)需配置線程池 - 使用
com.alicp.jetcache.redis.lettuce.LettuceConnectionManager
自定義管理器將與Redis連接的相關(guān)信息封裝成LettuceObjects
對(duì)象,并管理RedisClient與LettuceObjects對(duì)應(yīng)關(guān)系 - 相比Jedis更加安全高效
- 對(duì)Lettuce不了解的可以參考我寫的測(cè)試類
com.alicp.jetcache.test.external.LettuceTest
MultiLevelCache兩級(jí)緩存
當(dāng)你設(shè)置了緩存類型為BOTH兩級(jí)緩存,那么創(chuàng)建的實(shí)例對(duì)象會(huì)被封裝成com.alicp.jetcache.MultiLevelCache
對(duì)象
- 定義了
caches
字段類型為Cache[],用于保存AbstractEmbeddedCache本地緩存實(shí)例和AbstractExternalCache遠(yuǎn)程緩存實(shí)例,本地緩存存放于遠(yuǎn)程緩存前面 - 實(shí)現(xiàn)了
do_GET
方法,遍歷caches數(shù)組,也就是先從本地緩存獲取,如果獲取緩存不成功則從遠(yuǎn)程緩存獲取,成功獲取到緩存后會(huì)調(diào)用checkResultAndFillUpperCache方法 - 從
checkResultAndFillUpperCache
方法的邏輯可以看到,將獲取到的緩存數(shù)據(jù)更新至更底層的緩存中,也就是說(shuō)如果緩存數(shù)據(jù)是從遠(yuǎn)程獲取到的,那么進(jìn)入這個(gè)方法后會(huì)將獲取到的緩存數(shù)據(jù)更新到本地緩存中去,這樣下次請(qǐng)求可以直接從本地緩存獲取,避免與Redis之間的網(wǎng)絡(luò)消耗 - 實(shí)現(xiàn)了
do_PUT
方法,遍歷caches數(shù)組,通過(guò)CompletableFuture
進(jìn)行異步編程,將所有的操作綁定在一條鏈上執(zhí)行。 - 實(shí)現(xiàn)的了
PUT(K key, V value)
方法,會(huì)先判斷是否單獨(dú)配置了本地緩存時(shí)間localExipre,配置了則單獨(dú)為本地緩存設(shè)置過(guò)期時(shí)間,沒(méi)有配置則到期時(shí)間和遠(yuǎn)程緩存的一樣 - 覆蓋
tryLock
方法,調(diào)用caches[caches.length-1].tryLock方法,也就是只會(huì)調(diào)用最頂層遠(yuǎn)程緩存的這個(gè)方法
主要代碼如下:
public class MultiLevelCache<K, V> extends AbstractCache<K, V> { private Cache[] caches; private MultiLevelCacheConfig<K, V> config; @SuppressWarnings("unchecked") @Deprecated public MultiLevelCache(Cache... caches) throws CacheConfigException { this.caches = caches; checkCaches(); CacheConfig lastConfig = caches[caches.length - 1].config(); config = new MultiLevelCacheConfig<>(); config.setCaches(Arrays.asList(caches)); config.setExpireAfterWriteInMillis(lastConfig.getExpireAfterWriteInMillis()); config.setCacheNullValue(lastConfig.isCacheNullValue()); } @SuppressWarnings("unchecked") public MultiLevelCache(MultiLevelCacheConfig<K, V> cacheConfig) throws CacheConfigException { this.config = cacheConfig; this.caches = cacheConfig.getCaches().toArray(new Cache[]{}); checkCaches(); } private void checkCaches() { if (caches == null || caches.length == 0) { throw new IllegalArgumentException(); } for (Cache c : caches) { if (c.config().getLoader() != null) { throw new CacheConfigException("Loader on sub cache is not allowed, set the loader into MultiLevelCache."); } } } public Cache[] caches() { return caches; } @Override public MultiLevelCacheConfig<K, V> config() { return config; } @Override public CacheResult PUT(K key, V value) { if (config.isUseExpireOfSubCache()) { // 本地緩存使用自己的失效時(shí)間 // 設(shè)置了TimeUnit為null,本地緩存則使用自己的到期時(shí)間 return PUT(key, value, 0, null); } else { return PUT(key, value, config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS); } } @Override public CacheResult PUT_ALL(Map<? extends K, ? extends V> map) { if (config.isUseExpireOfSubCache()) { return PUT_ALL(map, 0, null); } else { return PUT_ALL(map, config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS); } } @Override protected CacheGetResult<V> do_GET(K key) { // 遍歷多級(jí)緩存(遠(yuǎn)程緩存排在后面) for (int i = 0; i < caches.length; i++) { Cache cache = caches[i]; CacheGetResult result = cache.GET(key); if (result.isSuccess()) { CacheValueHolder<V> holder = unwrapHolder(result.getHolder()); /* * 這個(gè)遍歷是從低層的緩存開始獲取,獲取成功則將該值設(shè)置到更低層的緩存中 * 情景: * 本地沒(méi)有獲取到緩存,遠(yuǎn)程獲取到了緩存,這里會(huì)將遠(yuǎn)程的緩存數(shù)據(jù)設(shè)置到本地中, * 這樣下次請(qǐng)求則直接從本次獲取,減少了遠(yuǎn)程獲取的時(shí)間 */ checkResultAndFillUpperCache(key, i, holder); return new CacheGetResult(CacheResultCode.SUCCESS, null, holder); } } return CacheGetResult.NOT_EXISTS_WITHOUT_MSG; } private CacheValueHolder<V> unwrapHolder(CacheValueHolder<V> h) { // if @Cached or @CacheCache change type from REMOTE to BOTH (or from BOTH to REMOTE), // during the dev/publish process, the value type which different application server put into cache server will be different // (CacheValueHolder<V> and CacheValueHolder<CacheValueHolder<V>>, respectively). // So we need correct the problem at here and in CacheGetResult. Objects.requireNonNull(h); if (h.getValue() instanceof CacheValueHolder) { return (CacheValueHolder<V>) h.getValue(); } else { return h; } } private void checkResultAndFillUpperCache(K key, int i, CacheValueHolder<V> h) { Objects.requireNonNull(h); long currentExpire = h.getExpireTime(); long now = System.currentTimeMillis(); if (now <= currentExpire) { if(config.isUseExpireOfSubCache()){ // 如果使用本地自己的緩存過(guò)期時(shí)間 // 使用本地緩存自己的過(guò)期時(shí)間 PUT_caches(i, key, h.getValue(), 0, null); } else { // 使用遠(yuǎn)程緩存的過(guò)期時(shí)間 long restTtl = currentExpire - now; if (restTtl > 0) { // 遠(yuǎn)程緩存數(shù)據(jù)還未失效,則重新設(shè)置本地的緩存 PUT_caches(i, key, h.getValue(), restTtl, TimeUnit.MILLISECONDS); } } } } @Override protected MultiGetResult<K, V> do_GET_ALL(Set<? extends K> keys) { HashMap<K, CacheGetResult<V>> resultMap = new HashMap<>(); Set<K> restKeys = new HashSet<>(keys); for (int i = 0; i < caches.length; i++) { if (restKeys.size() == 0) { break; } Cache<K, CacheValueHolder<V>> c = caches[i]; MultiGetResult<K, CacheValueHolder<V>> allResult = c.GET_ALL(restKeys); if (allResult.isSuccess() && allResult.getValues() != null) { for (Map.Entry<K, CacheGetResult<CacheValueHolder<V>>> en : allResult.getValues().entrySet()) { K key = en.getKey(); CacheGetResult result = en.getValue(); if (result.isSuccess()) { CacheValueHolder<V> holder = unwrapHolder(result.getHolder()); checkResultAndFillUpperCache(key, i, holder); resultMap.put(key, new CacheGetResult(CacheResultCode.SUCCESS, null, holder)); restKeys.remove(key); } } } } for (K k : restKeys) { resultMap.put(k, CacheGetResult.NOT_EXISTS_WITHOUT_MSG); } return new MultiGetResult<>(CacheResultCode.SUCCESS, null, resultMap); } @Override protected CacheResult do_PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit) { return PUT_caches(caches.length, key, value, expireAfterWrite, timeUnit); } @Override protected CacheResult do_PUT_ALL(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit) { CompletableFuture<ResultData> future = CompletableFuture.completedFuture(null); for (Cache c : caches) { CacheResult r; if(timeUnit == null) { r = c.PUT_ALL(map); } else { r = c.PUT_ALL(map, expireAfterWrite, timeUnit); } future = combine(future, r); } return new CacheResult(future); } private CacheResult PUT_caches(int lastIndex, K key, V value, long expire, TimeUnit timeUnit) { CompletableFuture<ResultData> future = CompletableFuture.completedFuture(null); for (int i = 0; i < lastIndex; i++) { Cache cache = caches[i]; CacheResult r; if (timeUnit == null) { // 表示本地緩存使用自己過(guò)期時(shí)間 r = cache.PUT(key, value); } else { r = cache.PUT(key, value, expire, timeUnit); } // 將多個(gè) PUT 操作放在一條鏈上 future = combine(future, r); } return new CacheResult(future); } private CompletableFuture<ResultData> combine(CompletableFuture<ResultData> future, CacheResult result) { return future.thenCombine(result.future(), (d1, d2) -> { if (d1 == null) { return d2; } if (d1.getResultCode() != d2.getResultCode()) { return new ResultData(CacheResultCode.PART_SUCCESS, null, null); } return d1; }); } @Override protected CacheResult do_REMOVE(K key) { CompletableFuture<ResultData> future = CompletableFuture.completedFuture(null); for (Cache cache : caches) { CacheResult r = cache.REMOVE(key); future = combine(future, r); } return new CacheResult(future); } @Override protected CacheResult do_REMOVE_ALL(Set<? extends K> keys) { CompletableFuture<ResultData> future = CompletableFuture.completedFuture(null); for (Cache cache : caches) { CacheResult r = cache.REMOVE_ALL(keys); future = combine(future, r); } return new CacheResult(future); } @Override public <T> T unwrap(Class<T> clazz) { Objects.requireNonNull(clazz); for (Cache cache : caches) { try { T obj = (T) cache.unwrap(clazz); if (obj != null) { return obj; } } catch (IllegalArgumentException e) { // ignore } } throw new IllegalArgumentException(clazz.getName()); } @Override public AutoReleaseLock tryLock(K key, long expire, TimeUnit timeUnit) { if (key == null) { return null; } return caches[caches.length - 1].tryLock(key, expire, timeUnit); } @Override public boolean putIfAbsent(K key, V value) { throw new UnsupportedOperationException("putIfAbsent is not supported by MultiLevelCache"); } @Override protected CacheResult do_PUT_IF_ABSENT(K key, V value, long expireAfterWrite, TimeUnit timeUnit) { throw new UnsupportedOperationException("PUT_IF_ABSENT is not supported by MultiLevelCache"); } @Override public void close() { for (Cache c : caches) { c.close(); } } }
RefreshCache
com.alicp.jetcache.RefreshCache
為緩存實(shí)例添加刷新任務(wù),前面在AbstractCache抽象類中講到了,在com.alicp.jetcache.anno.support.CacheContext.buildCache
方法中會(huì)將cache包裝成CacheHandlerRefreshCache
,所以說(shuō)每個(gè)緩存實(shí)例都會(huì)調(diào)用一下addOrUpdateRefreshTask
方法,代碼如下:
public class RefreshCache<K, V> extends LoadingCache<K, V> { protected CacheConfig<K, V> config; /** * 用于保存刷新任務(wù) */ private ConcurrentHashMap<Object, RefreshTask> taskMap = new ConcurrentHashMap<>(); protected void addOrUpdateRefreshTask(K key, CacheLoader<K, V> loader) { // 獲取緩存刷新策略 RefreshPolicy refreshPolicy = config.getRefreshPolicy(); if (refreshPolicy == null) { // 沒(méi)有則不進(jìn)行刷新 return; } // 獲取刷新時(shí)間間隔 long refreshMillis = refreshPolicy.getRefreshMillis(); if (refreshMillis > 0) { // 獲取線程任務(wù)的ID Object taskId = getTaskId(key); // 獲取對(duì)應(yīng)的RefreshTask,不存在則創(chuàng)建一個(gè) RefreshTask refreshTask = taskMap.computeIfAbsent(taskId, tid -> { logger.debug("add refresh task. interval={}, key={}", refreshMillis, key); RefreshTask task = new RefreshTask(taskId, key, loader); task.lastAccessTime = System.currentTimeMillis(); /* * 獲取 ScheduledExecutorService 周期/延遲線程池,10個(gè)核心線程,創(chuàng)建的線程都是守護(hù)線程 * scheduleWithFixedDelay(Runnable command, long initialDelay, long period, TimeUnit unit) * 運(yùn)行的任務(wù)task、多久延遲后開始執(zhí)行、后續(xù)執(zhí)行的周期間隔多長(zhǎng),時(shí)間單位 * 通過(guò)其創(chuàng)建一個(gè)循環(huán)任務(wù),用于刷新緩存數(shù)據(jù) */ ScheduledFuture<?> future = JetCacheExecutor.heavyIOExecutor().scheduleWithFixedDelay(task, refreshMillis, refreshMillis, TimeUnit.MILLISECONDS); task.future = future; return task; }); // 設(shè)置最后一次訪問(wèn)時(shí)間 refreshTask.lastAccessTime = System.currentTimeMillis(); } } }
如果緩存實(shí)例配置了刷新策略并且刷新間隔大于0,則會(huì)從taskMap
(線程安全)中嘗試獲取對(duì)應(yīng)的刷新任務(wù)RefreshTask
,如果不存在則創(chuàng)建一個(gè)任務(wù)放入線程池周期性的執(zhí)行
com.alicp.jetcache.RefreshCache.RefreshTask
代碼如下:
public class RefreshCache<K, V> extends LoadingCache<K, V> { protected Cache concreteCache() { Cache c = getTargetCache(); while (true) { if (c instanceof ProxyCache) { c = ((ProxyCache) c).getTargetCache(); } else if (c instanceof MultiLevelCache) { Cache[] caches = ((MultiLevelCache) c).caches(); // 如果是兩級(jí)緩存則返回遠(yuǎn)程緩存 c = caches[caches.length - 1]; } else { return c; } } } class RefreshTask implements Runnable { /** * 唯一標(biāo)志符,也就是Key轉(zhuǎn)換后的值 */ private Object taskId; /** * 緩存的Key */ private K key; /** * 執(zhí)行方法的CacheLoader對(duì)象 */ private CacheLoader<K, V> loader; /** * 最后一次訪問(wèn)時(shí)間 */ private long lastAccessTime; /** * 該 Task 的執(zhí)行策略 */ private ScheduledFuture future; RefreshTask(Object taskId, K key, CacheLoader<K, V> loader) { this.taskId = taskId; this.key = key; this.loader = loader; } private void cancel() { logger.debug("cancel refresh: {}", key); // 嘗試中斷當(dāng)前任務(wù) future.cancel(false); // 從任務(wù)列表中刪除 taskMap.remove(taskId); } /** * 重新加載數(shù)據(jù) * * @throws Throwable 異常 */ private void load() throws Throwable { CacheLoader<K, V> l = loader == null ? config.getLoader() : loader; if (l != null) { // 封裝 CacheLoader 成 ProxyLoader,加載后會(huì)發(fā)起 Load 事件 l = CacheUtil.createProxyLoader(cache, l, eventConsumer); // 加載 V v = l.load(key); if (needUpdate(v, l)) { // 將重新加載的數(shù)據(jù)放入緩存 cache.PUT(key, v); } } } /** * 遠(yuǎn)程加載數(shù)據(jù) * * @param concreteCache 緩存對(duì)象 * @param currentTime 當(dāng)前時(shí)間 * @throws Throwable 異常 */ private void externalLoad(final Cache concreteCache, final long currentTime) throws Throwable { // 獲取 Key 轉(zhuǎn)換后的值 byte[] newKey = ((AbstractExternalCache) concreteCache).buildKey(key); // 創(chuàng)建分布式鎖對(duì)應(yīng)的Key byte[] lockKey = combine(newKey, "_#RL#".getBytes()); // 分布式鎖的存在時(shí)間 long loadTimeOut = RefreshCache.this.config.getRefreshPolicy().getRefreshLockTimeoutMillis(); // 刷新間隔 long refreshMillis = config.getRefreshPolicy().getRefreshMillis(); // Key對(duì)應(yīng)的時(shí)間戳Key(用于存放上次刷新時(shí)間) byte[] timestampKey = combine(newKey, "_#TS#".getBytes()); // AbstractExternalCache buildKey method will not convert byte[] // 獲取Key上一次刷新時(shí)間 CacheGetResult refreshTimeResult = concreteCache.GET(timestampKey); boolean shouldLoad = false; // 是否需要重新加載 if (refreshTimeResult.isSuccess()) { // 當(dāng)前時(shí)間與上一次刷新的時(shí)間間隔是否大于或等于刷新間隔 shouldLoad = currentTime >= Long.parseLong(refreshTimeResult.getValue().toString()) + refreshMillis; } else if (refreshTimeResult.getResultCode() == CacheResultCode.NOT_EXISTS) { // 無(wú)緩存 shouldLoad = true; } if (!shouldLoad) { if (multiLevelCache) { // 將頂層的緩存數(shù)據(jù)更新至低層的緩存中,例如將遠(yuǎn)程的緩存數(shù)據(jù)放入本地緩存 // 因?yàn)槿绻嵌嗉?jí)緩存,創(chuàng)建刷新任務(wù)后,我們只需更新遠(yuǎn)程的緩存,然后從遠(yuǎn)程緩存獲取緩存數(shù)據(jù)更新低層的緩存,保證緩存一致 refreshUpperCaches(key); } return; } // 重新加載 Runnable r = () -> { try { load(); // AbstractExternalCache buildKey method will not convert byte[] // 保存一個(gè)key-value至redis,其中的信息為該value的生成時(shí)間,刷新緩存 concreteCache.put(timestampKey, String.valueOf(System.currentTimeMillis())); } catch (Throwable e) { throw new CacheException("refresh error", e); } }; // AbstractExternalCache buildKey method will not convert byte[] // 分布式緩存沒(méi)有一個(gè)全局分配的功能,這里嘗試獲取一把非嚴(yán)格的分布式鎖,獲取鎖的超時(shí)時(shí)間默認(rèn)60秒,也就是獲取到這把鎖最多可以擁有60秒 // 只有獲取Key對(duì)應(yīng)的這把分布式鎖,才執(zhí)行重新加載的操作 boolean lockSuccess = concreteCache.tryLockAndRun(lockKey, loadTimeOut, TimeUnit.MILLISECONDS, r); if (!lockSuccess && multiLevelCache) { // 沒(méi)有獲取到鎖并且是多級(jí)緩存 // 這個(gè)時(shí)候應(yīng)該有其他實(shí)例在刷新緩存,所以這里設(shè)置過(guò)一會(huì)直接獲取遠(yuǎn)程的緩存數(shù)據(jù)更新到本地 // 創(chuàng)建一個(gè)延遲任務(wù)(1/5刷新間隔后),將最頂層的緩存數(shù)據(jù)更新至每一層 JetCacheExecutor.heavyIOExecutor().schedule(() -> refreshUpperCaches(key), (long) (0.2 * refreshMillis), TimeUnit.MILLISECONDS); } } private void refreshUpperCaches(K key) { MultiLevelCache<K, V> targetCache = (MultiLevelCache<K, V>) getTargetCache(); Cache[] caches = targetCache.caches(); int len = caches.length; // 獲取多級(jí)緩存中頂層的緩存數(shù)據(jù) CacheGetResult cacheGetResult = caches[len - 1].GET(key); if (!cacheGetResult.isSuccess()) { return; } // 將緩存數(shù)據(jù)重新放入低層緩存 for (int i = 0; i < len - 1; i++) { caches[i].PUT(key, cacheGetResult.getValue()); } } /** * 刷新任務(wù)的具體執(zhí)行 */ @Override public void run() { try { if (config.getRefreshPolicy() == null || (loader == null && !hasLoader())) { // 取消執(zhí)行 cancel(); return; } long now = System.currentTimeMillis(); long stopRefreshAfterLastAccessMillis = config.getRefreshPolicy().getStopRefreshAfterLastAccessMillis(); if (stopRefreshAfterLastAccessMillis > 0) { // 最后一次訪問(wèn)到現(xiàn)在時(shí)間的間隔超過(guò)了設(shè)置的 stopRefreshAfterLastAccessMillis,則取消當(dāng)前任務(wù)執(zhí)行 if (lastAccessTime + stopRefreshAfterLastAccessMillis < now) { logger.debug("cancel refresh: {}", key); cancel(); return; } } logger.debug("refresh key: {}", key); // 獲取緩存實(shí)例對(duì)象,如果是多層則返回頂層,也就是遠(yuǎn)程緩存 Cache concreteCache = concreteCache(); if (concreteCache instanceof AbstractExternalCache) { // 遠(yuǎn)程緩存刷新 externalLoad(concreteCache, now); } else { // 本地緩存刷新 load(); } } catch (Throwable e) { logger.error("refresh error: key=" + key, e); } } } }
刷新邏輯:
- 判斷是否需要停止刷新了,需要的話調(diào)用其
future
的cancel方法取消執(zhí)行,并從taskMap
中刪除 - 獲取緩存實(shí)例對(duì)象,如果是多層則返回頂層,也就是遠(yuǎn)程緩存實(shí)例對(duì)象
- 如果是本地緩存,則調(diào)用
load
方法,也就是執(zhí)行l(wèi)oader函數(shù)加載原有方法,將獲取到的數(shù)據(jù)更新至緩存實(shí)例中(如果是多級(jí)緩存,則每級(jí)緩存都會(huì)更新) - 如果是遠(yuǎn)程緩存對(duì)象,則調(diào)用
externalLoad
方法,刷新后會(huì)往Redis中存放一個(gè)鍵值對(duì),key為key_#TS#
,value為上一次刷新時(shí)間
先從Redis中獲取上一次刷新時(shí)間的鍵值對(duì),根據(jù)上一次刷新的時(shí)間判斷是否大于刷新間隔,大于(或者沒(méi)有上一次刷新時(shí)間)表示需要重新加載數(shù)據(jù),否則不需要重新加載數(shù)據(jù)
如果不需要重新加載數(shù)據(jù),但是又是多級(jí)緩存,則獲取遠(yuǎn)程緩存數(shù)據(jù)更新至本地緩存,保證兩級(jí)緩存的一致性
如果需要重新加載數(shù)據(jù),則調(diào)用tryLockAndRun
方法,嘗試獲取分布式鎖,執(zhí)行刷新任務(wù)(調(diào)用load
方法,并往Redis中重新設(shè)置上一次的刷新時(shí)間),如果沒(méi)有獲取到分布式鎖,則創(chuàng)建一個(gè)延遲任務(wù)(1/5刷新間隔后)將最頂層的緩存數(shù)據(jù)更新至每一層
解析配置
主要查看jetcache-autoconfigure子模塊,解析application.yml中jetcache相關(guān)配置,初始化不同緩存類型的CacheBuilder
構(gòu)造器,用于生產(chǎn)緩存實(shí)例,也初始化以下對(duì)象:
com.alicp.jetcache.anno.support.ConfigProvider
:緩存管理器,注入了全局配置GlobalCacheConfig、緩存實(shí)例管理器SimpleCacheManager、緩存上下文CacheContext等大量信息
com.alicp.jetcache.autoconfigure.AutoConfigureBeans
:存儲(chǔ)CacheBuilder
構(gòu)造器以及Redis的相關(guān)信息
com.alicp.jetcache.anno.support.GlobalCacheConfig
:全局配置類,保存了一些全局信息
初始化構(gòu)造器
通過(guò)@Conditional
注解將需要使用到的緩存類型對(duì)應(yīng)的構(gòu)造器初始化類注入到Spring容器并執(zhí)行初始化過(guò)程,也就是創(chuàng)建CacheBuilder構(gòu)造器
初始化構(gòu)造器類的類型結(jié)構(gòu)如下圖所示:
主要對(duì)象描述:
AbstractCacheAutoInit:抽象類,實(shí)現(xiàn)Spring的InitializingBean接口,注入至Spring容器時(shí)完成初始化
EmbeddedCacheAutoInit:抽象類,繼承AbstractCacheAutoInit,解析本地緩存獨(dú)有的配置
LinkedHashMapAutoConfiguration:初始化LinkedHashMapCacheBuilder構(gòu)造器
CaffeineAutoConfiguration:初始化CaffeineCacheBuilder構(gòu)造器
ExternalCacheAutoInit:抽象類,繼承AbstractCacheAutoInit,解析遠(yuǎn)程緩存獨(dú)有的配置
RedisAutoInit:初始化RedisCacheBuilder構(gòu)造器
RedisLettuceAutoInit:初始化RedisLettuceCacheBuilder構(gòu)造器
AbstractCacheAutoInit
com.alicp.jetcache.autoconfigure.AbstractCacheAutoInit
抽象類主要實(shí)現(xiàn)了Spring的InitializingBean接口,在注入Spring容器時(shí),Spring會(huì)調(diào)用其afterPropertiesSet方法,完成本地緩存類型和遠(yuǎn)程緩存類型CacheBuilder
構(gòu)造器的初始化,主要代碼如下:
public abstract class AbstractCacheAutoInit implements InitializingBean { @Autowired protected ConfigurableEnvironment environment; @Autowired protected AutoConfigureBeans autoConfigureBeans; @Autowired protected ConfigProvider configProvider; protected String[] typeNames; private boolean inited = false; public AbstractCacheAutoInit(String... cacheTypes) { Objects.requireNonNull(cacheTypes,"cacheTypes can't be null"); Assert.isTrue(cacheTypes.length > 0, "cacheTypes length is 0"); this.typeNames = cacheTypes; } /** * 初始化方法 */ @Override public void afterPropertiesSet() { if (!inited) { synchronized (this) { if (!inited) { // 這里我們有兩個(gè)指定前綴 'jetcache.local' 'jetcache.remote' process("jetcache.local.", autoConfigureBeans.getLocalCacheBuilders(), true); process("jetcache.remote.", autoConfigureBeans.getRemoteCacheBuilders(), false); inited = true; } } } } private void process(String prefix, Map cacheBuilders, boolean local) { // 創(chuàng)建一個(gè)配置對(duì)象(本地或者遠(yuǎn)程) ConfigTree resolver = new ConfigTree(environment, prefix); // 獲取本地或者遠(yuǎn)程的配置項(xiàng) Map<String, Object> m = resolver.getProperties(); // 獲取本地或者遠(yuǎn)程的 area ,這里我一般只有默認(rèn)的 default Set<String> cacheAreaNames = resolver.directChildrenKeys(); for (String cacheArea : cacheAreaNames) { // 獲取本地或者遠(yuǎn)程存儲(chǔ)類型,例如 caffeine,redis.lettuce final Object configType = m.get(cacheArea + ".type"); // 緩存類型是否和當(dāng)前 CacheAutoInit 的某一個(gè) typeName 匹配(不同的 CacheAutoInit 會(huì)設(shè)置一個(gè)或者多個(gè) typename) boolean match = Arrays.stream(typeNames).anyMatch((tn) -> tn.equals(configType)); /* * 因?yàn)橛泻芏?CacheAutoInit 繼承者,都會(huì)執(zhí)行這個(gè)方法,不同的繼承者解析不同的配置 * 例如 CaffeineAutoConfiguration 只解析 jetcache.local.default.type=caffeine 即可 * RedisLettuceAutoInit 只解析 jetcache.remote.default.type=redis.lettuce 即可 */ if (!match) { continue; } // 獲取本地或者遠(yuǎn)程的 area 的子配置項(xiàng) ConfigTree ct = resolver.subTree(cacheArea + "."); logger.info("init cache area {} , type= {}", cacheArea, typeNames[0]); // 根據(jù)配置信息構(gòu)建本地或者遠(yuǎn)程緩存的 CacheBuilder 構(gòu)造器 CacheBuilder c = initCache(ct, local ? "local." + cacheArea : "remote." + cacheArea); // 將 CacheBuilder 構(gòu)造器存放至 AutoConfigureBeans cacheBuilders.put(cacheArea, c); } } /** * 設(shè)置公共的配置到 CacheBuilder 構(gòu)造器中 * * @param builder 構(gòu)造器 * @param ct 配置信息 */ protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) { AbstractCacheBuilder acb = (AbstractCacheBuilder) builder; // 設(shè)置 Key 的轉(zhuǎn)換函數(shù) acb.keyConvertor(configProvider.parseKeyConvertor(ct.getProperty("keyConvertor"))); // 設(shè)置超時(shí)時(shí)間 String expireAfterWriteInMillis = ct.getProperty("expireAfterWriteInMillis"); if (expireAfterWriteInMillis == null) { // compatible with 2.1 兼容老版本 expireAfterWriteInMillis = ct.getProperty("defaultExpireInMillis"); } if (expireAfterWriteInMillis != null) { acb.setExpireAfterWriteInMillis(Long.parseLong(expireAfterWriteInMillis)); } // 多長(zhǎng)時(shí)間沒(méi)有訪問(wèn)就讓緩存失效,0表示不使用該功能(注意:只支持本地緩存) String expireAfterAccessInMillis = ct.getProperty("expireAfterAccessInMillis"); if (expireAfterAccessInMillis != null) { acb.setExpireAfterAccessInMillis(Long.parseLong(expireAfterAccessInMillis)); } } /** * 初始化 CacheBuilder 構(gòu)造器交由子類去實(shí)現(xiàn) * * @param ct 配置信息 * @param cacheAreaWithPrefix 配置前綴 * @return CacheBuilder 構(gòu)造器 */ protected abstract CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix); }
1.在afterPropertiesSet()
方法中可以看到會(huì)調(diào)用process
方法分別初始化本地緩存和遠(yuǎn)程緩存的構(gòu)造器
2.定義的process
方法:
- 首先會(huì)從當(dāng)前環(huán)境中解析出JetCache的相關(guān)配置到ConfigTree對(duì)象中
- 然后遍歷緩存區(qū)域,獲取對(duì)應(yīng)的緩存類型type,進(jìn)行不同類型的緩存實(shí)例CacheBuilder構(gòu)造器初始化過(guò)程
- 不同CacheBuilder構(gòu)造器的初始化方法
initCache
交由子類實(shí)現(xiàn) - 獲取到CacheBuilder構(gòu)造器后會(huì)將其放入
AutoConfigureBeans
對(duì)象中去
3.另外也定義了parseGeneralConfig
方法解析本地緩存和遠(yuǎn)程緩存都有的配置至CacheBuilder構(gòu)造器中
EmbeddedCacheAutoInit
com.alicp.jetcache.autoconfigure.EmbeddedCacheAutoInit
抽象類繼承了AbstractCacheAutoInit
,主要是覆蓋父類的parseGeneralConfig
,解析本地緩存單有的配置limit
,代碼如下:
public abstract class EmbeddedCacheAutoInit extends AbstractCacheAutoInit { public EmbeddedCacheAutoInit(String... cacheTypes) { super(cacheTypes); } @Override protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) { super.parseGeneralConfig(builder, ct); EmbeddedCacheBuilder ecb = (EmbeddedCacheBuilder) builder; // 設(shè)置本地緩存每個(gè)緩存實(shí)例的緩存數(shù)量個(gè)數(shù)限制(默認(rèn)100) ecb.limit(Integer.parseInt(ct.getProperty("limit", String.valueOf(CacheConsts.DEFAULT_LOCAL_LIMIT)))); } }
LinkedHashMapAutoConfiguration
com.alicp.jetcache.autoconfigure.LinkedHashMapAutoConfiguration
繼承了EmbeddedCacheAutoInit
,實(shí)現(xiàn)了initCache
方法,先通過(guò)LinkedHashMapCacheBuilder創(chuàng)建一個(gè)默認(rèn)實(shí)現(xiàn)類,然后解析相關(guān)配置至構(gòu)造器中完成初始化,代碼如下:
@Component @Conditional(LinkedHashMapAutoConfiguration.LinkedHashMapCondition.class) public class LinkedHashMapAutoConfiguration extends EmbeddedCacheAutoInit { public LinkedHashMapAutoConfiguration() { super("linkedhashmap"); } @Override protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) { // 創(chuàng)建一個(gè) LinkedHashMapCacheBuilder 構(gòu)造器 LinkedHashMapCacheBuilder builder = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder(); // 解析相關(guān)配置至 LinkedHashMapCacheBuilder 的 CacheConfig 中 parseGeneralConfig(builder, ct); return builder; } public static class LinkedHashMapCondition extends JetCacheCondition { // 配置了緩存類型為 linkedhashmap 當(dāng)前類才會(huì)被注入 Spring 容器 public LinkedHashMapCondition() { super("linkedhashmap"); } } }
- 這里我們注意到
@Conditional
注解,這個(gè)注解的作用是:滿足SpringBootCondition
條件這個(gè)Bean才會(huì)被Spring容器管理 - 他的條件是
LinkedHashMapCondition
,繼承了JetCacheCondition
,也就是說(shuō)配置文件中配置了緩存類型為linkedhashmap
時(shí)這個(gè)類才會(huì)被Spring容器管理,才會(huì)完成LinkedHashMapCacheBuilder構(gòu)造器的初始化 JetCacheCondition
邏輯并不復(fù)雜,可自行查看
CaffeineAutoConfiguration
com.alicp.jetcache.autoconfigure.CaffeineAutoConfiguration
繼承了EmbeddedCacheAutoInit
,實(shí)現(xiàn)了initCache
方法,先通過(guò)CaffeineCacheBuilder創(chuàng)建一個(gè)默認(rèn)實(shí)現(xiàn)類,然后解析相關(guān)配置至構(gòu)造器中完成初始化,代碼如下:
@Component @Conditional(CaffeineAutoConfiguration.CaffeineCondition.class) public class CaffeineAutoConfiguration extends EmbeddedCacheAutoInit { public CaffeineAutoConfiguration() { super("caffeine"); } @Override protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) { // 創(chuàng)建一個(gè) CaffeineCacheBuilder 構(gòu)造器 CaffeineCacheBuilder builder = CaffeineCacheBuilder.createCaffeineCacheBuilder(); // 解析相關(guān)配置至 CaffeineCacheBuilder 的 CacheConfig 中 parseGeneralConfig(builder, ct); return builder; } public static class CaffeineCondition extends JetCacheCondition { // 配置了緩存類型為 caffeine 當(dāng)前類才會(huì)被注入 Spring 容器 public CaffeineCondition() { super("caffeine"); } } }
- 同樣使用了
@Conditional
注解,這個(gè)注解的作用是:滿足SpringBootCondition
條件這個(gè)Bean才會(huì)被Spring容器管理 - 他的條件是
CaffeineCondition
,繼承了JetCacheCondition
,也就是說(shuō)配置文件中配置了緩存類型為caffeine
時(shí)這個(gè)類才會(huì)被Spring容器管理,才會(huì)完成LinkedHashMapCacheBuilder構(gòu)造器的初始化
ExternalCacheAutoInit
com.alicp.jetcache.autoconfigure.ExternalCacheAutoInit
抽象類繼承了AbstractCacheAutoInit
,主要是覆蓋父類的parseGeneralConfig
,解析遠(yuǎn)程緩存單有的配置keyPrefix
、valueEncoder
和valueDecoder
,代碼如下:
public abstract class ExternalCacheAutoInit extends AbstractCacheAutoInit { public ExternalCacheAutoInit(String... cacheTypes) { super(cacheTypes); } /** * 設(shè)置遠(yuǎn)程緩存 CacheBuilder 構(gòu)造器的相關(guān)配置 * * @param builder 構(gòu)造器 * @param ct 配置信息 */ @Override protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) { super.parseGeneralConfig(builder, ct); ExternalCacheBuilder ecb = (ExternalCacheBuilder) builder; // 設(shè)置遠(yuǎn)程緩存 key 的前綴 ecb.setKeyPrefix(ct.getProperty("keyPrefix")); /* * 根據(jù)配置創(chuàng)建緩存數(shù)據(jù)的編碼函數(shù)和解碼函數(shù) */ ecb.setValueEncoder(configProvider.parseValueEncoder(ct.getProperty("valueEncoder", CacheConsts.DEFAULT_SERIAL_POLICY))); ecb.setValueDecoder(configProvider.parseValueDecoder(ct.getProperty("valueDecoder", CacheConsts.DEFAULT_SERIAL_POLICY))); } }
RedisAutoInit
com.alicp.jetcache.autoconfigure.RedisAutoInit
繼承了ExternalCacheAutoInit
,實(shí)現(xiàn)initCache
方法,完成了通過(guò)Jedis連接Redis的初始化操作,主要代碼如下:
@Configuration @Conditional(RedisAutoConfiguration.RedisCondition.class) public class RedisAutoConfiguration { public static final String AUTO_INIT_BEAN_NAME = "redisAutoInit"; @Bean(name = AUTO_INIT_BEAN_NAME) public RedisAutoInit redisAutoInit() { return new RedisAutoInit(); } public static class RedisCondition extends JetCacheCondition { // 配置了緩存類型為 redis 當(dāng)前類才會(huì)被注入 Spring 容器 public RedisCondition() { super("redis"); } } public static class RedisAutoInit extends ExternalCacheAutoInit { public RedisAutoInit() { // 設(shè)置緩存類型 super("redis"); } @Autowired private AutoConfigureBeans autoConfigureBeans; @Override protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) { Pool jedisPool = parsePool(ct); Pool[] slavesPool = null; int[] slavesPoolWeights = null; // 是否只從 Redis 的從節(jié)點(diǎn)讀取數(shù)據(jù) boolean readFromSlave = Boolean.parseBoolean(ct.getProperty("readFromSlave", "False")); // 獲取從節(jié)點(diǎn)的配置信息 ConfigTree slaves = ct.subTree("slaves."); Set<String> slaveNames = slaves.directChildrenKeys(); // 依次創(chuàng)建每個(gè)從節(jié)點(diǎn)的連接池 if (slaveNames.size() > 0) { slavesPool = new Pool[slaveNames.size()]; slavesPoolWeights = new int[slaveNames.size()]; int i = 0; for (String slaveName: slaveNames) { ConfigTree slaveConfig = slaves.subTree(slaveName + "."); slavesPool[i] = parsePool(slaveConfig); slavesPoolWeights[i] = Integer.parseInt(slaveConfig.getProperty("weight","100")); i++; } } // 創(chuàng)建一個(gè) RedisCacheBuilder 構(gòu)造器 ExternalCacheBuilder externalCacheBuilder = RedisCacheBuilder.createRedisCacheBuilder() .jedisPool(jedisPool) .readFromSlave(readFromSlave) .jedisSlavePools(slavesPool) .slaveReadWeights(slavesPoolWeights); // 解析相關(guān)配置至 RedisCacheBuilder 的 CacheConfig 中 parseGeneralConfig(externalCacheBuilder, ct); // eg: "jedisPool.remote.default" autoConfigureBeans.getCustomContainer().put("jedisPool." + cacheAreaWithPrefix, jedisPool); return externalCacheBuilder; } /** * 創(chuàng)建 Redis 連接池 * * @param ct 配置信息 * @return 連接池 */ private Pool<Jedis> parsePool(ConfigTree ct) { // 創(chuàng)建連接池配置對(duì)象 GenericObjectPoolConfig poolConfig = parsePoolConfig(ct); String host = ct.getProperty("host", (String) null); int port = Integer.parseInt(ct.getProperty("port", "0")); int timeout = Integer.parseInt(ct.getProperty("timeout", String.valueOf(Protocol.DEFAULT_TIMEOUT))); String password = ct.getProperty("password", (String) null); int database = Integer.parseInt(ct.getProperty("database", String.valueOf(Protocol.DEFAULT_DATABASE))); String clientName = ct.getProperty("clientName", (String) null); boolean ssl = Boolean.parseBoolean(ct.getProperty("ssl", "false")); String masterName = ct.getProperty("masterName", (String) null); String sentinels = ct.getProperty("sentinels", (String) null);//ip1:port,ip2:port Pool<Jedis> jedisPool; if (sentinels == null) { Objects.requireNonNull(host, "host/port or sentinels/masterName is required"); if (port == 0) { throw new IllegalStateException("host/port or sentinels/masterName is required"); } // 創(chuàng)建一個(gè) Jedis 連接池 jedisPool = new JedisPool(poolConfig, host, port, timeout, password, database, clientName, ssl); } else { Objects.requireNonNull(masterName, "host/port or sentinels/masterName is required"); String[] strings = sentinels.split(","); HashSet<String> sentinelsSet = new HashSet<>(); for (String s : strings) { if (s != null && !s.trim().equals("")) { sentinelsSet.add(s.trim()); } } // 創(chuàng)建一個(gè) Jedis Sentine 連接池 jedisPool = new JedisSentinelPool(masterName, sentinelsSet, poolConfig, timeout, password, database, clientName); } return jedisPool; } } }
com.alicp.jetcache.autoconfigure.RedisAutoInit
是com.alicp.jetcache.autoconfigure.RedisAutoConfiguration
內(nèi)部的靜態(tài)類,在RedisAutoConfiguration內(nèi)通過(guò)redisAutoInit()
方法定義RedisAutoInit作為Spring Bean
同樣RedisAutoConfiguration使用了@Conditional
注解,滿足SpringBootCondition
條件這個(gè)Bean才會(huì)被Spring容器管理,內(nèi)部的RedisAutoInit也不會(huì)被管理,也就是說(shuō)配置文件中配置了緩存類型為redis
時(shí)RedisLettuceAutoInit才會(huì)被Spring容器管理,才會(huì)完成RedisLettuceCacheBuilder構(gòu)造器的初始化
實(shí)現(xiàn)了initCache
方法
- 先解析Redis的相關(guān)配置
- 通過(guò)Jedis創(chuàng)建Redis連接池
- 通過(guò)RedisCacheBuilder創(chuàng)建一個(gè)默認(rèn)實(shí)現(xiàn)類
- 解析相關(guān)配置至構(gòu)造器中完成初始化
- 將Redis連接保存至
AutoConfigureBeans
中
RedisLettuceAutoInit
com.alicp.jetcache.autoconfigure.RedisLettuceAutoInit
繼承了ExternalCacheAutoInit
,實(shí)現(xiàn)initCache
方法,完成了通過(guò)Lettuce連接Redis的初始化操作,主要代碼如下:
@Configuration @Conditional(RedisLettuceAutoConfiguration.RedisLettuceCondition.class) public class RedisLettuceAutoConfiguration { public static final String AUTO_INIT_BEAN_NAME = "redisLettuceAutoInit"; /** * 注入 spring 容器的條件 */ public static class RedisLettuceCondition extends JetCacheCondition { // 配置了緩存類型為 redis.lettuce 當(dāng)前類才會(huì)被注入 Spring 容器 public RedisLettuceCondition() { super("redis.lettuce"); } } @Bean(name = {AUTO_INIT_BEAN_NAME}) public RedisLettuceAutoInit redisLettuceAutoInit() { return new RedisLettuceAutoInit(); } public static class RedisLettuceAutoInit extends ExternalCacheAutoInit { public RedisLettuceAutoInit() { // 設(shè)置緩存類型 super("redis.lettuce"); } /** * 初始化 RedisLettuceCacheBuilder 構(gòu)造器 * * @param ct 配置信息 * @param cacheAreaWithPrefix 配置前綴 * @return 構(gòu)造器 */ @Override protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) { Map<String, Object> map = ct.subTree("uri"/*there is no dot*/).getProperties(); // 數(shù)據(jù)節(jié)點(diǎn)偏好設(shè)置 String readFromStr = ct.getProperty("readFrom"); // 集群模式 String mode = ct.getProperty("mode"); // 異步獲取結(jié)果的超時(shí)時(shí)間,默認(rèn)1s long asyncResultTimeoutInMillis = Long.parseLong( ct.getProperty("asyncResultTimeoutInMillis", Long.toString(CacheConsts.ASYNC_RESULT_TIMEOUT.toMillis()))); ReadFrom readFrom = null; if (readFromStr != null) { /* * MASTER:只從Master節(jié)點(diǎn)中讀取。 * MASTER_PREFERRED:優(yōu)先從Master節(jié)點(diǎn)中讀取。 * SLAVE_PREFERRED:優(yōu)先從Slave節(jié)點(diǎn)中讀取。 * SLAVE:只從Slave節(jié)點(diǎn)中讀取。 * NEAREST:使用最近一次連接的Redis實(shí)例讀取。 */ readFrom = ReadFrom.valueOf(readFromStr.trim()); } AbstractRedisClient client; StatefulConnection connection = null; if (map == null || map.size() == 0) { throw new CacheConfigException("lettuce uri is required"); } else { // 創(chuàng)建對(duì)應(yīng)的 RedisURI List<RedisURI> uriList = map.values().stream().map((k) -> RedisURI.create(URI.create(k.toString()))) .collect(Collectors.toList()); if (uriList.size() == 1) { // 只有一個(gè) URI,集群模式只給一個(gè)域名怎么辦 TODO 疑問(wèn)?? RedisURI uri = uriList.get(0); if (readFrom == null) { // 創(chuàng)建一個(gè) Redis 客戶端 client = RedisClient.create(uri); // 設(shè)置失去連接時(shí)的行為,拒絕命令,默認(rèn)為 DEFAULT ((RedisClient) client).setOptions(ClientOptions.builder(). disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS).build()); } else { // 創(chuàng)建一個(gè) Redis 客戶端 client = RedisClient.create(); ((RedisClient) client).setOptions(ClientOptions.builder(). disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS).build()); // 創(chuàng)建一個(gè)安全連接并設(shè)置數(shù)據(jù)節(jié)點(diǎn)偏好 StatefulRedisMasterSlaveConnection c = MasterSlave.connect( (RedisClient) client, new JetCacheCodec(), uri); c.setReadFrom(readFrom); connection = c; } } else { // 多個(gè) URI,集群模式 if (mode != null && mode.equalsIgnoreCase("MasterSlave")) { client = RedisClient.create(); ((RedisClient) client).setOptions(ClientOptions.builder(). disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS).build()); StatefulRedisMasterSlaveConnection c = MasterSlave.connect( (RedisClient) client, new JetCacheCodec(), uriList); if (readFrom != null) { c.setReadFrom(readFrom); } connection = c; } else { // 創(chuàng)建一個(gè) Redis 客戶端 client = RedisClusterClient.create(uriList); ((RedisClusterClient) client).setOptions(ClusterClientOptions.builder(). disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS).build()); if (readFrom != null) { StatefulRedisClusterConnection c = ((RedisClusterClient) client).connect(new JetCacheCodec()); c.setReadFrom(readFrom); connection = c; } } } } // 創(chuàng)建一個(gè) RedisLettuceCacheBuilder 構(gòu)造器 ExternalCacheBuilder externalCacheBuilder = RedisLettuceCacheBuilder.createRedisLettuceCacheBuilder() .connection(connection) .redisClient(client) .asyncResultTimeoutInMillis(asyncResultTimeoutInMillis); // 解析相關(guān)配置至 RedisLettuceCacheBuilder 的 CacheConfig 中 parseGeneralConfig(externalCacheBuilder, ct); // eg: "remote.default.client" autoConfigureBeans.getCustomContainer().put(cacheAreaWithPrefix + ".client", client); // 開始將 Redis 客戶端和安全連接保存至 LettuceConnectionManager 管理器中 LettuceConnectionManager m = LettuceConnectionManager.defaultManager(); // 初始化 Lettuce 連接 Redis m.init(client, connection); // 初始化 Redis 連接的相關(guān)信息保存至 LettuceObjects 中,并將相關(guān)信息保存至 AutoConfigureBeans.customContainer autoConfigureBeans.getCustomContainer().put(cacheAreaWithPrefix + ".connection", m.connection(client)); autoConfigureBeans.getCustomContainer().put(cacheAreaWithPrefix + ".commands", m.commands(client)); autoConfigureBeans.getCustomContainer().put(cacheAreaWithPrefix + ".asyncCommands", m.asyncCommands(client)); autoConfigureBeans.getCustomContainer().put(cacheAreaWithPrefix + ".reactiveCommands", m.reactiveCommands(client)); return externalCacheBuilder; } } }
1.com.alicp.jetcache.autoconfigure.RedisLettuceAutoInit
是com.alicp.jetcache.autoconfigure.RedisLettuceAutoConfiguration
內(nèi)部的靜態(tài)類,在RedisLettuceAutoConfiguration內(nèi)通過(guò)redisLettuceAutoInit()
方法定義RedisLettuceAutoInit作為Spring Bean
2.同樣RedisLettuceAutoConfiguration使用了@Conditional
注解,滿足SpringBootCondition
條件這個(gè)Bean才會(huì)被Spring容器管理,內(nèi)部的RedisLettuceAutoInit也不會(huì)被管理,也就是說(shuō)配置文件中配置了緩存類型為redis.lettuce
時(shí)RedisLettuceAutoInit才會(huì)被Spring容器管理,才會(huì)完成RedisLettuceCacheBuilder構(gòu)造器的初始化
3.實(shí)現(xiàn)了initCache
方法
- 先解析Redis的相關(guān)配置
- 通過(guò)Lettuce創(chuàng)建Redis客戶端和與Redis的連接
- 通過(guò)RedisLettuceCacheBuilder創(chuàng)建一個(gè)默認(rèn)實(shí)現(xiàn)類
- 解析相關(guān)配置至構(gòu)造器中完成初始化
- 獲取
LettuceConnectionManager
管理器,將通過(guò)Lettuce創(chuàng)建Redis客戶端和與Redis的連接保存 - 將Redis客戶端、與Redis的連接、同步命令、異步命令和反應(yīng)式命令相關(guān)保存至
AutoConfigureBeans
中
JetCacheAutoConfiguration自動(dòng)配置
上面的初始化構(gòu)造器的類需要被Spring容器管理,就需被掃描到,我們一般會(huì)設(shè)置掃描路徑,但是別人引入JetCache肯定是作為其他包不能夠被掃描到的,這些Bean也就不會(huì)被Spring管理,這里我們查看jetcache-autoconfigure
模塊下src/main/resources/META-INF/spring.factories
文件,內(nèi)容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.alicp.jetcache.autoconfigure.JetCacheAutoConfiguration
這應(yīng)該是一種SPI
機(jī)制,這樣這個(gè)項(xiàng)目以外的JetCache包里面的com.alicp.jetcache.autoconfigure.JetCacheAutoConfiguration
就會(huì)被Spring容器掃描到,我們來(lái)看看他的代碼:
/** * 該 Bean 將會(huì)被 Spring 容器注入,依次注入下面幾個(gè) Bean * SpringConfigProvider -> AutoConfigureBeans -> BeanDependencyManager(為 GlobalCacheConfig 添加 CacheAutoInit 依賴) -> GlobalCacheConfig * 由此會(huì)完成初始化配置操作,緩存實(shí)例構(gòu)造器 CacheBuilder 也會(huì)被注入容器 * * Created on 2016/11/17. * * @author <a href="mailto:areyouok@gmail.com">huangli</a> */ @Configuration @ConditionalOnClass(GlobalCacheConfig.class) @ConditionalOnMissingBean(GlobalCacheConfig.class) @EnableConfigurationProperties(JetCacheProperties.class) @Import({RedisAutoConfiguration.class, CaffeineAutoConfiguration.class, MockRemoteCacheAutoConfiguration.class, LinkedHashMapAutoConfiguration.class, RedisLettuceAutoConfiguration.class, RedisSpringDataAutoConfiguration.class}) public class JetCacheAutoConfiguration { public static final String GLOBAL_CACHE_CONFIG_NAME = "globalCacheConfig"; private SpringConfigProvider _springConfigProvider = new SpringConfigProvider(); private AutoConfigureBeans _autoConfigureBeans = new AutoConfigureBeans(); private GlobalCacheConfig _globalCacheConfig; @Bean @ConditionalOnMissingBean public SpringConfigProvider springConfigProvider() { return _springConfigProvider; } @Bean public AutoConfigureBeans autoConfigureBeans() { return _autoConfigureBeans; } @Bean public static BeanDependencyManager beanDependencyManager(){ return new BeanDependencyManager(); } @Bean(name = GLOBAL_CACHE_CONFIG_NAME) public GlobalCacheConfig globalCacheConfig(SpringConfigProvider configProvider, AutoConfigureBeans autoConfigureBeans, JetCacheProperties props) { if (_globalCacheConfig != null) { return _globalCacheConfig; } _globalCacheConfig = new GlobalCacheConfig(); _globalCacheConfig.setHiddenPackages(props.getHiddenPackages()); _globalCacheConfig.setStatIntervalMinutes(props.getStatIntervalMinutes()); _globalCacheConfig.setAreaInCacheName(props.isAreaInCacheName()); _globalCacheConfig.setPenetrationProtect(props.isPenetrationProtect()); _globalCacheConfig.setEnableMethodCache(props.isEnableMethodCache()); _globalCacheConfig.setLocalCacheBuilders(autoConfigureBeans.getLocalCacheBuilders()); _globalCacheConfig.setRemoteCacheBuilders(autoConfigureBeans.getRemoteCacheBuilders()); return _globalCacheConfig; } }
- 可以看到通過(guò)
@Import
注解,初始化構(gòu)造器的那些類會(huì)被加入到Spring容器,加上@Condotional
注解,只有我們配置過(guò)的緩存類型的構(gòu)造器才會(huì)被加入,然后保存至AutoConfigureBeans對(duì)象中 - 注意到這里我們注入的是
SpringConfigProvider
對(duì)象,加上@ConditionalOnMissingBean
注解,無(wú)法再次注冊(cè)該對(duì)象至Spring容器,相比ConfigProvider
對(duì)象,它的區(qū)別是設(shè)置了EncoderParser為DefaultSpringEncoderParser,設(shè)置了KeyConvertorParser為DefaultSpringKeyConvertorParser,目的是支持兩個(gè)解析器能夠解析自定義bean - 在
BeanDependencyManager
中可以看到它是一個(gè)BeanFactoryPostProcessor
,用于BeanFactory容器初始后執(zhí)行操作,目的是往JetCacheAutoConfiguration的BeanDefinition的依賴中添加幾個(gè)AbstractCacheAutoInit類型的beanName,保證幾個(gè)CacheBuilder構(gòu)造器已經(jīng)初始化 globalCacheConfig
方法中設(shè)置全局的相關(guān)配置并添加已經(jīng)初始化的CacheBuilder構(gòu)造器,然后返回GlobalCacheConfig讓Spring容器管理,這樣一來(lái)就完成了JetCache的解析配置并初始化的功能
CacheBuilder構(gòu)造器
構(gòu)造器的作用就是根據(jù)配置構(gòu)建一個(gè)對(duì)應(yīng)類型的緩存實(shí)例
CacheBuilder的子類結(jié)構(gòu)如下:
根據(jù)類名就可以知道其作用
CacheBuilder接口只定義了一個(gè)buildCache()
方法,用于構(gòu)建緩存實(shí)例,交由不同的實(shí)現(xiàn)類
AbstractCacheBuilder抽象類實(shí)現(xiàn)了buildCache()
方法,主要代碼如下:
public abstract class AbstractCacheBuilder<T extends AbstractCacheBuilder<T>> implements CacheBuilder, Cloneable { /** * 該緩存實(shí)例的配置 */ protected CacheConfig config; /** * 創(chuàng)建緩存實(shí)例函數(shù) */ private Function<CacheConfig, Cache> buildFunc; public abstract CacheConfig getConfig(); protected T self() { return (T) this; } public T buildFunc(Function<CacheConfig, Cache> buildFunc) { this.buildFunc = buildFunc; return self(); } protected void beforeBuild() { } @Deprecated public final <K, V> Cache<K, V> build() { return buildCache(); } @Override public final <K, V> Cache<K, V> buildCache() { if (buildFunc == null) { throw new CacheConfigException("no buildFunc"); } beforeBuild(); // 克隆一份配置信息,因?yàn)檫@里獲取到的是全局配置信息,以防后續(xù)被修改 CacheConfig c = getConfig().clone(); // 通過(guò)構(gòu)建函數(shù)創(chuàng)建一個(gè)緩存實(shí)例 Cache<K, V> cache = buildFunc.apply(c); /* * 目前發(fā)現(xiàn) c.getLoader() 都是 null,后續(xù)都會(huì)把 cache 封裝成 CacheHandlerRefreshCache * TODO 疑問(wèn)???? */ if (c.getLoader() != null) { if (c.getRefreshPolicy() == null) { cache = new LoadingCache<>(cache); } else { cache = new RefreshCache<>(cache); } } return cache; } @Override public Object clone() { AbstractCacheBuilder copy = null; try { copy = (AbstractCacheBuilder) super.clone(); copy.config = getConfig().clone(); return copy; } catch (CloneNotSupportedException e) { throw new CacheException(e); } } }
- 實(shí)現(xiàn)了
java.lang.Cloneable
的clone方法,支持克隆該對(duì)象,因?yàn)槊總€(gè)緩存實(shí)例的配置不一定相同,這個(gè)構(gòu)造器中保存的是全局的一些配置,所以需要克隆一個(gè)構(gòu)造器出來(lái)為每個(gè)緩存實(shí)例設(shè)置其自己的配置而不影響這個(gè)最初始的構(gòu)造器 - 定義CacheConfig對(duì)象存放緩存配置,構(gòu)建緩存實(shí)例需要根據(jù)這些配置
- 定義的
buildFunc
函數(shù)用于構(gòu)建緩存實(shí)例,我們?cè)诔跏蓟瘶?gòu)造器中可以看到,不同的構(gòu)造器設(shè)置的該函數(shù)都是new一個(gè)緩存實(shí)例并傳入配置信息,例如:
// 設(shè)置構(gòu)建 CaffeineCache 緩存實(shí)例的函數(shù) buildFunc((c) -> new CaffeineCache((EmbeddedCacheConfig) c)); // 進(jìn)入CaffeineCache的構(gòu)造器你就可以看到會(huì)根據(jù)配置完成緩存實(shí)例的初始化
不同類型的構(gòu)造器區(qū)別在于CacheConfig類型不同,因?yàn)檫h(yuǎn)程和本地的配置是有所區(qū)別的,還有就是設(shè)置的buildFunc
函數(shù)不同,因?yàn)樾枰獦?gòu)建不同的緩存實(shí)例,和上面的例子差不多,都是new一個(gè)緩存實(shí)例并傳入配置信息,這里就不一一講述了
AOP
主要查看jetcache-anno子模塊,提供AOP功能
啟用JetCache
JetCache可以通過(guò)@EnableMethodCache和@EnableCreateCacheAnnotation注解完成AOP的初始化工作,我們?cè)赟pring Boot工程中的啟動(dòng)類上面添加這兩個(gè)注解即可啟用JetCache緩存。
@EnableMethodCache
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({CommonConfiguration.class, ConfigSelector.class}) public @interface EnableMethodCache { boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; String[] basePackages(); }
注解的相關(guān)配置在上面的'如何使用'中已經(jīng)講過(guò)了,這里我們關(guān)注@Import
注解中的CommonConfiguration
和ConfigSelector
兩個(gè)類,將會(huì)被Spring容器管理
com.alicp.jetcache.anno.config.CommonConfiguration
上面有@Configuration注解,所以會(huì)被作為一個(gè)Spring Bean,里面定義了一個(gè)Bean為ConfigMap
,所以這個(gè)Bean也會(huì)被Spring容器管理,com.alicp.jetcache.anno.support.ConfigMap
中保存方法與緩存注解配置信息的映射關(guān)系com.alicp.jetcache.anno.config.ConfigSelector
繼承了AdviceModeImportSelector,通過(guò)@Import
注解他的selectImports
方法會(huì)被調(diào)用,根據(jù)不同的AdviceMode導(dǎo)入不同的配置類,可以看到會(huì)返回一個(gè)JetCacheProxyConfiguration類名稱,那么它也會(huì)被注入
com.alicp.jetcache.anno.config.JetCacheProxyConfiguration
是配置AOP的配置類,代碼如下:
@Configuration public class JetCacheProxyConfiguration implements ImportAware, ApplicationContextAware { protected AnnotationAttributes enableMethodCache; private ApplicationContext applicationContext; @Override public void setImportMetadata(AnnotationMetadata importMetadata) { // 獲取 @EnableMethodCache 注解信息 this.enableMethodCache = AnnotationAttributes.fromMap( importMetadata.getAnnotationAttributes(EnableMethodCache.class.getName(), false)); if (this.enableMethodCache == null) { throw new IllegalArgumentException( "@EnableMethodCache is not present on importing class " + importMetadata.getClassName()); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Bean(name = CacheAdvisor.CACHE_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public CacheAdvisor jetcacheAdvisor(JetCacheInterceptor jetCacheInterceptor) { CacheAdvisor advisor = new CacheAdvisor(); // bean的名稱:jetcache2.internalCacheAdvisor advisor.setAdviceBeanName(CacheAdvisor.CACHE_ADVISOR_BEAN_NAME); // 設(shè)置緩存攔截器為 JetCacheInterceptor advisor.setAdvice(jetCacheInterceptor); // 設(shè)置需要掃描的包 advisor.setBasePackages(this.enableMethodCache.getStringArray("basePackages")); // 設(shè)置優(yōu)先級(jí),默認(rèn) Integer 的最大值,最低優(yōu)先級(jí) advisor.setOrder(this.enableMethodCache.<Integer>getNumber("order")); return advisor; } /** * 注入一個(gè) JetCacheInterceptor 攔截器,設(shè)置為框架內(nèi)部的角色 * * @return JetCacheInterceptor */ @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public JetCacheInterceptor jetCacheInterceptor() { return new JetCacheInterceptor(); } }
因?yàn)镴etCacheProxyConfiguration是通過(guò)@Import
注解注入的并且實(shí)現(xiàn)了ImportAware
接口,當(dāng)被注入Bean的時(shí)候會(huì)先調(diào)用其setImportMetadata
方法(這里好像必須添加@Configuration注解,不然無(wú)法被Spring識(shí)別出來(lái))獲取到@EnableMethodCache
注解的元信息
其中定義了兩個(gè)Bean:
com.alicp.jetcache.anno.aop.JetCacheInterceptor
:實(shí)現(xiàn)了aop中的MethodInterceptor方法攔截器,可用于aop攔截方法后執(zhí)行相關(guān)處理
com.alicp.jetcache.anno.aop.CacheAdvisor
:
1.繼承了org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor
,將會(huì)作為一個(gè)AOP切面
2.設(shè)置了通知advice為JetCacheInterceptor,也就是說(shuō)被攔截的方法都會(huì)進(jìn)入JetCacheInterceptor,JetCacheInterceptor就作為JetCache的入口了
3.根據(jù)注解設(shè)置了需要掃描的包路徑以及優(yōu)先級(jí),默認(rèn)是最低優(yōu)先級(jí)
4.CacheAdvisor實(shí)現(xiàn)了org.springframework.aopPointcutAdvisor
接口的getPointcut()
方法,設(shè)置這個(gè)切面的切入點(diǎn)為com.alicp.jetcache.anno.aop.CachePointcut
5.從CachePointcut作為切入點(diǎn)
- 實(shí)現(xiàn)了
org.springframework.aop.ClassFilter
接口,用于判斷哪些類需要被攔截 - 實(shí)現(xiàn)了
org.springframework.aop.MethodMatcher
接口,用于判斷哪些類中的哪些方法會(huì)被攔截 - 在判斷方法是否需要進(jìn)入JetCache的JetCacheInterceptor過(guò)程中,會(huì)解析方法上面的JetCache相關(guān)緩存注解,將配置信息封裝
com.alicp.jetcache.anno.methodCacheInvokeConfig
對(duì)象中,并把它保存至com.alicp.jetcache.anno.support.ConfigMap
對(duì)象中
總結(jié):@EnableMethodCache注解主要就是生成一個(gè)AOP切面用于攔截帶有緩存注解的方法
@EnableCreateCacheAnnotation
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({CommonConfiguration.class, CreateCacheAnnotationBeanPostProcessor.class}) public @interface EnableCreateCacheAnnotation { }
相比@EnableMethodCache注解,沒(méi)有相關(guān)屬性,同樣會(huì)導(dǎo)入CommonConfiguration類
不同的是將導(dǎo)入com.alicp.jetcache.anno.field.CreateCacheAnnotationBeanPostProcessor
類,它繼承了org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
作為一個(gè)BeanPostProcessor,用于在Spring初始化bean的時(shí)候做一些操作
從代碼中可以看到他的作用是:如果這個(gè)bean內(nèi)部存在添加了帶有@CreateCache
注解的字段(沒(méi)有添加static),會(huì)將這個(gè)字段作為需要注入的對(duì)象,解析成 com.alicp.jetcache.anno.field.LazyInitCache
緩存實(shí)例
LazyInitCache的主要代碼如下:
class LazyInitCache implements ProxyCache { /** * 是否初始化,用于懶加載 */ private boolean inited; /** * 緩存實(shí)例 */ private Cache cache; /** * 所處上下文 */ private ConfigurableListableBeanFactory beanFactory; /** * CreateCache 注解元信息 */ private CreateCache ann; /** * 字段 */ private Field field; /** * 刷新策略 */ private RefreshPolicy refreshPolicy; /** * 保護(hù)策略 */ private PenetrationProtectConfig protectConfig; public LazyInitCache(ConfigurableListableBeanFactory beanFactory, CreateCache ann, Field field) { this.beanFactory = beanFactory; this.ann = ann; this.field = field; CacheRefresh cr = field.getAnnotation(CacheRefresh.class); if (cr != null) { refreshPolicy = CacheConfigUtil.parseRefreshPolicy(cr); } CachePenetrationProtect penetrateProtect = field.getAnnotation(CachePenetrationProtect.class); if (penetrateProtect != null) { protectConfig = CacheConfigUtil.parsePenetrationProtectConfig(penetrateProtect); } } private void checkInit() { if (!inited) { synchronized (this) { if (!inited) { init(); inited = true; } } } } /** * 獲取緩存實(shí)例,不存在則新建 * * @return 緩存實(shí)例 */ @Override public Cache getTargetCache() { checkInit(); return cache; } private void init() { if (inited) { throw new IllegalStateException(); } // 從 spring 的容器中獲取全局緩存配置 GlobalCacheConfig 對(duì)象 GlobalCacheConfig globalCacheConfig = beanFactory.getBean(GlobalCacheConfig.class); ConfigProvider configProvider = beanFactory.getBean(ConfigProvider.class); // 將注解信息封裝到 CachedAnnoConfig 對(duì)象中 CachedAnnoConfig cac = new CachedAnnoConfig(); cac.setArea(ann.area()); cac.setName(ann.name()); cac.setTimeUnit(ann.timeUnit()); cac.setExpire(ann.expire()); cac.setLocalExpire(ann.localExpire()); cac.setCacheType(ann.cacheType()); cac.setLocalLimit(ann.localLimit()); cac.setSerialPolicy(ann.serialPolicy()); cac.setKeyConvertor(ann.keyConvertor()); cac.setRefreshPolicy(refreshPolicy); cac.setPenetrationProtectConfig(protectConfig); String cacheName = cac.getName(); if (CacheConsts.isUndefined(cacheName)) { String[] hiddenPackages = globalCacheConfig.getHiddenPackages(); CacheNameGenerator g = configProvider.createCacheNameGenerator(hiddenPackages); cacheName = g.generateCacheName(field); } // 從緩存實(shí)例管理器中獲取或者創(chuàng)建對(duì)應(yīng)的緩存實(shí)例 cache = configProvider.getCacheContext().__createOrGetCache(cac, ann.area(), cacheName); } }
1.可以看到通過(guò)@CreateCache
創(chuàng)建的緩存實(shí)例也可以添加@CacheRefresh
和@CachePenetrationProtect
注解
2.在AbstractCache抽象類的computeIfAbsentImpl方法中我們有講到,如果緩存實(shí)例是ProxyCache類型,則會(huì)先調(diào)用其getTargetCache()
方法獲取緩存實(shí)例對(duì)象,所以LazyInitCache在第一次訪問(wèn)的時(shí)候才進(jìn)行初始化,并根據(jù)緩存注解配置信息創(chuàng)建(存在則直接獲?。┮粋€(gè)緩存實(shí)例
總結(jié):@EnableCreateCacheAnnotation注解主要是支持@CreateCache能夠創(chuàng)建緩存實(shí)例
通過(guò)@EnableMethodCache
和@EnableCreateCacheAnnotation
兩個(gè)注解,加上前面的解析配置過(guò)程
,已經(jīng)完成的JetCache的解析與初始化過(guò)程,那么接下來(lái)我們來(lái)看看JetCache如何處理被攔截的方法。
攔截器
從com.alicp.jetcache.anno.aop.CachePointcut
切入點(diǎn)判斷方法是否需要攔截的邏輯:
1.方法所在的類對(duì)象是否匹配,除去以"java"、"org.springframework"開頭和包含"$$EnhancerBySpringCGLIB$$"、"$$FastClassBySpringCGLIB$$"的類,該類是否在我們通過(guò)@EnableMethodCache
注解配置的basePackages中
2.從ConfigMap
獲取方法對(duì)應(yīng)的CacheInvokeConfig
對(duì)象,也就是獲取緩存配置信息
- 如果是一個(gè)空對(duì)象,那么不需要被攔截,因?yàn)榍懊嬉呀?jīng)判斷了所在的類是否需要被攔截,而這個(gè)類中并不是所有的方法都會(huì)添加緩存注解,所以這一類的方法會(huì)設(shè)置一個(gè)空對(duì)象(定義在CacheInvokeConfig內(nèi)部的一個(gè)靜態(tài)對(duì)象添加了final修飾),保存在ConfigMap中
- 如果不為null,則需被攔截
- 通過(guò)CacheConfigUtil解析這個(gè)方法的緩存注解,如果有@Cached注解或者@CacheInvalidate注解或者@CacheUpdate注解,先解析注解生成CacheInvokeConfig對(duì)象保存至ConfigMap中,然后該方法會(huì)被攔截,否在保存一個(gè)空對(duì)象不會(huì)被攔截
ConfigProvider
com.alicp.jetcache.anno.support.ConfigProvide
是一個(gè)配置提供者對(duì)象,包含了JetCache的全局配置、緩存實(shí)例管理器、緩存value轉(zhuǎn)換器、緩存key轉(zhuǎn)換器、上下文和監(jiān)控指標(biāo)相關(guān)信息,主要代碼如下:
public class ConfigProvider extends AbstractLifecycle { /** * 緩存的全局配置 */ @Resource protected GlobalCacheConfig globalCacheConfig; /** * 緩存實(shí)例管理器 */ protected SimpleCacheManager cacheManager; /** * 根據(jù)不同類型生成緩存數(shù)據(jù)轉(zhuǎn)換函數(shù)的轉(zhuǎn)換器 */ protected EncoderParser encoderParser; /** * 根據(jù)不同類型生成緩存 Key 轉(zhuǎn)換函數(shù)的轉(zhuǎn)換器 */ protected KeyConvertorParser keyConvertorParser; /** * 緩存監(jiān)控指標(biāo)管理器 */ protected CacheMonitorManager cacheMonitorManager; /** * 打印緩存各項(xiàng)指標(biāo)的函數(shù) */ private Consumer<StatInfo> metricsCallback = new StatInfoLogger(false); /** * 緩存更新事件(REMOVE OR PUT)消息接收者,無(wú)實(shí)現(xiàn)類 * 我們可以自己實(shí)現(xiàn) CacheMessagePublisher 用于統(tǒng)計(jì)一些緩存的命中信息 */ private CacheMessagePublisher cacheMessagePublisher; /** * 默認(rèn)的緩存監(jiān)控指標(biāo)管理器 */ private CacheMonitorManager defaultCacheMonitorManager = new DefaultCacheMonitorManager(); /** * 緩存上下文 */ private CacheContext cacheContext; public ConfigProvider() { cacheManager = SimpleCacheManager.defaultManager; encoderParser = new DefaultEncoderParser(); keyConvertorParser = new DefaultKeyConvertorParser(); cacheMonitorManager = defaultCacheMonitorManager; } @Override public void doInit() { // 啟動(dòng)緩存指標(biāo)監(jiān)控器,周期性打印各項(xiàng)指標(biāo) initDefaultCacheMonitorInstaller(); // 初始化緩存上下文 cacheContext = newContext(); } protected void initDefaultCacheMonitorInstaller() { if (cacheMonitorManager == defaultCacheMonitorManager) { DefaultCacheMonitorManager installer = (DefaultCacheMonitorManager) cacheMonitorManager; installer.setGlobalCacheConfig(globalCacheConfig); installer.setMetricsCallback(metricsCallback); if (cacheMessagePublisher != null) { installer.setCacheMessagePublisher(cacheMessagePublisher); } // 啟動(dòng)緩存指標(biāo)監(jiān)控器 installer.init(); } } @Override public void doShutdown() { shutdownDefaultCacheMonitorInstaller(); cacheManager.rebuild(); } protected void shutdownDefaultCacheMonitorInstaller() { if (cacheMonitorManager == defaultCacheMonitorManager) { ((DefaultCacheMonitorManager) cacheMonitorManager).shutdown(); } } /** * 根據(jù)編碼類型通過(guò)緩存value轉(zhuǎn)換器生成編碼函數(shù) * * @param valueEncoder 編碼類型 * @return 編碼函數(shù) */ public Function<Object, byte[]> parseValueEncoder(String valueEncoder) { return encoderParser.parseEncoder(valueEncoder); } /** * 根據(jù)解碼類型通過(guò)緩存value轉(zhuǎn)換器生成解碼函數(shù) * * @param valueDecoder 解碼類型 * @return 解碼函數(shù) */ public Function<byte[], Object> parseValueDecoder(String valueDecoder) { return encoderParser.parseDecoder(valueDecoder); } /** * 根據(jù)轉(zhuǎn)換類型通過(guò)緩存key轉(zhuǎn)換器生成轉(zhuǎn)換函數(shù) * * @param convertor 轉(zhuǎn)換類型 * @return 轉(zhuǎn)換函數(shù) */ public Function<Object, Object> parseKeyConvertor(String convertor) { return keyConvertorParser.parseKeyConvertor(convertor); } public CacheNameGenerator createCacheNameGenerator(String[] hiddenPackages) { return new DefaultCacheNameGenerator(hiddenPackages); } protected CacheContext newContext() { return new CacheContext(this, globalCacheConfig); } }
繼承了com.alicp.jetcache.anno.support.AbstractLifecycle
,查看其代碼可以看到有兩個(gè)方法,分別為init()
初始化方法和shutdown()
銷毀方法,因?yàn)榉謩e添加了@PostConstruct
注解和@PreDestroy
注解,所以在Spring初始化時(shí)會(huì)調(diào)用init(),在Spring容器銷毀時(shí)會(huì)調(diào)用shutdown()方法,內(nèi)部分別調(diào)用doInit()和doShutdown(),這兩個(gè)方法交由子類實(shí)現(xiàn)
在doInit()方法中先啟動(dòng)緩存指標(biāo)監(jiān)控器,用于周期性打印各項(xiàng)緩存指標(biāo),然后初始化CacheContext緩存上下文,SpringConfigProvider返回的是SpringConfigContext
在doShutdown()方法中關(guān)閉緩存指標(biāo)監(jiān)控器,清除緩存實(shí)例
CacheContext
com.alicp.jetcache.anno.support.CacheContext
緩存上下文主要為每一個(gè)被攔截的請(qǐng)求創(chuàng)建緩存上下文,構(gòu)建對(duì)應(yīng)的緩存實(shí)例,主要代碼如下:
public class CacheContext { private static Logger logger = LoggerFactory.getLogger(CacheContext.class); private static ThreadLocal<CacheThreadLocal> cacheThreadLocal = new ThreadLocal<CacheThreadLocal>() { @Override protected CacheThreadLocal initialValue() { return new CacheThreadLocal(); } }; /** * JetCache 緩存的管理器(包含很多信息) */ private ConfigProvider configProvider; /** * 緩存的全局配置 */ private GlobalCacheConfig globalCacheConfig; /** * 緩存實(shí)例管理器 */ protected SimpleCacheManager cacheManager; public CacheContext(ConfigProvider configProvider, GlobalCacheConfig globalCacheConfig) { this.globalCacheConfig = globalCacheConfig; this.configProvider = configProvider; cacheManager = configProvider.getCacheManager(); } public CacheInvokeContext createCacheInvokeContext(ConfigMap configMap) { // 創(chuàng)建一個(gè)本次調(diào)用的上下文 CacheInvokeContext c = newCacheInvokeContext(); // 添加一個(gè)函數(shù),后續(xù)用于獲取緩存實(shí)例 // 根據(jù)注解配置信息獲取緩存實(shí)例對(duì)象,不存在則創(chuàng)建并設(shè)置到緩存注解配置類中 c.setCacheFunction((invokeContext, cacheAnnoConfig) -> { Cache cache = cacheAnnoConfig.getCache(); if (cache == null) { if (cacheAnnoConfig instanceof CachedAnnoConfig) { // 緩存注解 // 根據(jù)配置創(chuàng)建一個(gè)緩存實(shí)例對(duì)象,通過(guò) CacheBuilder cache = createCacheByCachedConfig((CachedAnnoConfig) cacheAnnoConfig, invokeContext); } else if ((cacheAnnoConfig instanceof CacheInvalidateAnnoConfig) || (cacheAnnoConfig instanceof CacheUpdateAnnoConfig)) { // 更新/使失效緩存注解 CacheInvokeConfig cacheDefineConfig = configMap.getByCacheName(cacheAnnoConfig.getArea(), cacheAnnoConfig.getName()); if (cacheDefineConfig == null) { String message = "can't find @Cached definition with area=" + cacheAnnoConfig.getArea() + " name=" + cacheAnnoConfig.getName() + ", specified in " + cacheAnnoConfig.getDefineMethod(); CacheConfigException e = new CacheConfigException(message); logger.error("Cache operation aborted because can't find @Cached definition", e); return null; } cache = createCacheByCachedConfig(cacheDefineConfig.getCachedAnnoConfig(), invokeContext); } cacheAnnoConfig.setCache(cache); } return cache; }); return c; } private Cache createCacheByCachedConfig(CachedAnnoConfig ac, CacheInvokeContext invokeContext) { // 緩存區(qū)域 String area = ac.getArea(); // 緩存實(shí)例名稱 String cacheName = ac.getName(); if (CacheConsts.isUndefined(cacheName)) { // 沒(méi)有定義緩存實(shí)例名稱 // 生成緩存實(shí)例名稱:類名+方法名+(參數(shù)類型) cacheName = configProvider.createCacheNameGenerator(invokeContext.getHiddenPackages()) .generateCacheName(invokeContext.getMethod(), invokeContext.getTargetObject()); } // 創(chuàng)建緩存實(shí)例對(duì)象 Cache cache = __createOrGetCache(ac, area, cacheName); return cache; } @Deprecated public <K, V> Cache<K, V> getCache(String cacheName) { return getCache(CacheConsts.DEFAULT_AREA, cacheName); } @Deprecated public <K, V> Cache<K, V> getCache(String area, String cacheName) { Cache cache = cacheManager.getCacheWithoutCreate(area, cacheName); return cache; } public Cache __createOrGetCache(CachedAnnoConfig cachedAnnoConfig, String area, String cacheName) { // 緩存名稱拼接 String fullCacheName = area + "_" + cacheName; // 從緩存實(shí)例管理器中根據(jù)緩存區(qū)域和緩存實(shí)例名稱獲取緩存實(shí)例 Cache cache = cacheManager.getCacheWithoutCreate(area, cacheName); if (cache == null) { synchronized (this) { // 加鎖 // 再次確認(rèn) cache = cacheManager.getCacheWithoutCreate(area, cacheName); if (cache == null) { /* * 緩存區(qū)域的名稱是否作為緩存 key 名稱前綴,默認(rèn)為 true ,我一般設(shè)置為 false */ if (globalCacheConfig.isAreaInCacheName()) { // for compatible reason, if we use default configuration, the prefix should same to that version <=2.4.3 cache = buildCache(cachedAnnoConfig, area, fullCacheName); } else { // 構(gòu)建一個(gè)緩存實(shí)例 cache = buildCache(cachedAnnoConfig, area, cacheName); } cacheManager.putCache(area, cacheName, cache); } } } return cache; } protected Cache buildCache(CachedAnnoConfig cachedAnnoConfig, String area, String cacheName) { Cache cache; if (cachedAnnoConfig.getCacheType() == CacheType.LOCAL) { // 本地緩存 cache = buildLocal(cachedAnnoConfig, area); } else if (cachedAnnoConfig.getCacheType() == CacheType.REMOTE) { // 遠(yuǎn)程緩存 cache = buildRemote(cachedAnnoConfig, area, cacheName); } else { // 兩級(jí)緩存 // 構(gòu)建本地緩存實(shí)例 Cache local = buildLocal(cachedAnnoConfig, area); // 構(gòu)建遠(yuǎn)程緩存實(shí)例 Cache remote = buildRemote(cachedAnnoConfig, area, cacheName); // 兩級(jí)緩存時(shí)是否單獨(dú)設(shè)置了本地緩存失效時(shí)間 localExpire boolean useExpireOfSubCache = cachedAnnoConfig.getLocalExpire() > 0; // 創(chuàng)建一個(gè)兩級(jí)緩存CacheBuilder cache = MultiLevelCacheBuilder.createMultiLevelCacheBuilder() .expireAfterWrite(remote.config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS) .addCache(local, remote) .useExpireOfSubCache(useExpireOfSubCache) .cacheNullValue(cachedAnnoConfig.isCacheNullValue()) .buildCache(); } // 設(shè)置緩存刷新策略 cache.config().setRefreshPolicy(cachedAnnoConfig.getRefreshPolicy()); // 將 cache 封裝成 CacheHandlerRefreshCache,也就是 RefreshCache 類型 // 后續(xù)添加刷新任務(wù)時(shí)會(huì)判斷是否為 RefreshCache 類型,然后決定是否執(zhí)行 addOrUpdateRefreshTask 方法,添加刷新任務(wù),沒(méi)有刷新策略不會(huì)添加 cache = new CacheHandler.CacheHandlerRefreshCache(cache); // 設(shè)置緩存未命中時(shí),JVM是否只允許一個(gè)線程執(zhí)行方法,其他線程等待,全局配置默認(rèn)為false cache.config().setCachePenetrationProtect(globalCacheConfig.isPenetrationProtect()); PenetrationProtectConfig protectConfig = cachedAnnoConfig.getPenetrationProtectConfig(); if (protectConfig != null) { // 方法的@CachePenetrationProtect注解 cache.config().setCachePenetrationProtect(protectConfig.isPenetrationProtect()); cache.config().setPenetrationProtectTimeout(protectConfig.getPenetrationProtectTimeout()); } if (configProvider.getCacheMonitorManager() != null) { // 添加監(jiān)控統(tǒng)計(jì)配置 configProvider.getCacheMonitorManager().addMonitors(area, cacheName, cache); } return cache; } protected Cache buildRemote(CachedAnnoConfig cachedAnnoConfig, String area, String cacheName) { // 獲取緩存區(qū)域?qū)?yīng)的 CacheBuilder 構(gòu)造器 ExternalCacheBuilder cacheBuilder = (ExternalCacheBuilder) globalCacheConfig.getRemoteCacheBuilders().get(area); if (cacheBuilder == null) { throw new CacheConfigException("no remote cache builder: " + area); } // 克隆一個(gè) CacheBuilder 構(gòu)造器,因?yàn)椴煌彺鎸?shí)例有不同的配置 cacheBuilder = (ExternalCacheBuilder) cacheBuilder.clone(); if (cachedAnnoConfig.getExpire() > 0 ) { // 設(shè)置失效時(shí)間 cacheBuilder.expireAfterWrite(cachedAnnoConfig.getExpire(), cachedAnnoConfig.getTimeUnit()); } // 設(shè)置緩存 key 的前綴 if (cacheBuilder.getConfig().getKeyPrefix() != null) { // 配置文件中配置了 prefix,則設(shè)置為 prefix+cacheName cacheBuilder.setKeyPrefix(cacheBuilder.getConfig().getKeyPrefix() + cacheName); } else { // 設(shè)置為 cacheName cacheBuilder.setKeyPrefix(cacheName); } if (!CacheConsts.isUndefined(cachedAnnoConfig.getKeyConvertor())) { // 如果注解中設(shè)置了Key的轉(zhuǎn)換方式則替換,否則還是使用全局的 // 設(shè)置 key 的轉(zhuǎn)換器,只支持 FASTJSON cacheBuilder.setKeyConvertor(configProvider.parseKeyConvertor(cachedAnnoConfig.getKeyConvertor())); } if (!CacheConsts.isUndefined(cachedAnnoConfig.getSerialPolicy())) { // 緩存數(shù)據(jù)保存至遠(yuǎn)程需要進(jìn)行編碼和解碼,所以這里設(shè)置其編碼和解碼方式,KRYO 和 JAVA 可選擇 cacheBuilder.setValueEncoder(configProvider.parseValueEncoder(cachedAnnoConfig.getSerialPolicy())); cacheBuilder.setValueDecoder(configProvider.parseValueDecoder(cachedAnnoConfig.getSerialPolicy())); } // 設(shè)置是否緩存 null 值 cacheBuilder.setCacheNullValue(cachedAnnoConfig.isCacheNullValue()); return cacheBuilder.buildCache(); } protected Cache buildLocal(CachedAnnoConfig cachedAnnoConfig, String area) { // 獲取緩存區(qū)域?qū)?yīng)的 CacheBuilder 構(gòu)造器 EmbeddedCacheBuilder cacheBuilder = (EmbeddedCacheBuilder) globalCacheConfig.getLocalCacheBuilders().get(area); if (cacheBuilder == null) { throw new CacheConfigException("no local cache builder: " + area); } // 克隆一個(gè) CacheBuilder 構(gòu)造器,因?yàn)椴煌彺鎸?shí)例有不同的配置 cacheBuilder = (EmbeddedCacheBuilder) cacheBuilder.clone(); if (cachedAnnoConfig.getLocalLimit() != CacheConsts.UNDEFINED_INT) { // 本地緩存數(shù)量限制 cacheBuilder.setLimit(cachedAnnoConfig.getLocalLimit()); } if (cachedAnnoConfig.getCacheType() == CacheType.BOTH && cachedAnnoConfig.getLocalExpire() > 0) { // 設(shè)置本地緩存失效時(shí)間,前提是多級(jí)緩存,一般和遠(yuǎn)程緩存保持一致不設(shè)置 cacheBuilder.expireAfterWrite(cachedAnnoConfig.getLocalExpire(), cachedAnnoConfig.getTimeUnit()); } else if (cachedAnnoConfig.getExpire() > 0) { // 設(shè)置失效時(shí)間 cacheBuilder.expireAfterWrite(cachedAnnoConfig.getExpire(), cachedAnnoConfig.getTimeUnit()); } if (!CacheConsts.isUndefined(cachedAnnoConfig.getKeyConvertor())) { cacheBuilder.setKeyConvertor(configProvider.parseKeyConvertor(cachedAnnoConfig.getKeyConvertor())); } // 設(shè)置是否緩存 null 值 cacheBuilder.setCacheNullValue(cachedAnnoConfig.isCacheNullValue()); // 構(gòu)建一個(gè)緩存實(shí)例 return cacheBuilder.buildCache(); } protected CacheInvokeContext newCacheInvokeContext() { return new CacheInvokeContext(); } }
createCacheInvokeContext
方法返回一個(gè)本次調(diào)用的上下文CacheInvokeContext,為這個(gè)上下文設(shè)置緩存函數(shù),用于獲取或者構(gòu)建緩存實(shí)例,這個(gè)函數(shù)在CacheHandler中會(huì)被調(diào)用,我們來(lái)看看這個(gè)函數(shù)的處理邏輯:有兩個(gè)入?yún)?,分別為本次調(diào)用的上下文和緩存注解的配置信息
首先從緩存注解的配置信息中獲取緩存實(shí)例,如果不為null則直接返回,否則調(diào)用createCacheByCachedConfig
方法,根據(jù)配置通過(guò)CacheBuilder構(gòu)造器創(chuàng)建一個(gè)緩存實(shí)例對(duì)象
createCacheByCachedConfig
方法:
1.如果沒(méi)有定義緩存實(shí)例名稱(@Cached注解中的name配置),則生成類名+方法名+(參數(shù)類型)
作為緩存實(shí)例名稱
2.然后調(diào)用__createOrGetCache
方法
__createOrGetCache
方法:
1.通過(guò)緩存實(shí)例管理器SimpleCacheManager根據(jù)緩存區(qū)域area和緩存實(shí)例名稱cacheName獲取緩存實(shí)例對(duì)象,如果不為null則直接返回,判斷緩存實(shí)例對(duì)象是否為null為進(jìn)行兩次確認(rèn),第二次會(huì)給當(dāng)前CacheContext加鎖進(jìn)行判斷,避免線程不安全
2.緩存實(shí)例對(duì)象還是為null的話,先判斷緩存區(qū)域area是否添加至緩存實(shí)例名稱中,是的話"area_cacheName"為緩存實(shí)例名稱,然后調(diào)用buildCache
方法創(chuàng)建一個(gè)緩存實(shí)例對(duì)象
buildCache
方法:根據(jù)緩存實(shí)例類型構(gòu)建不同的緩存實(shí)例對(duì)象,處理邏輯如下:
CacheType為LOCAL
則調(diào)用buildLocal
方法:
- 1.1. 從GlobalCacheConfig全局配置的localCacheBuilders(保存本地緩存CacheBuilder構(gòu)造器的集合)中的獲取本地緩存該緩存區(qū)域的構(gòu)造器,在之前講到的'JetCacheAutoConfiguration自動(dòng)配置'中有說(shuō)到過(guò),會(huì)將初始化好的構(gòu)造器從AutoConfigureBeans中添加至GlobalCacheConfig中
- 1.2. 克隆一個(gè) CacheBuilder 構(gòu)造器,因?yàn)椴煌彺鎸?shí)例有不同的配置
- 1.3. 將緩存注解的配置信息設(shè)置到構(gòu)造器中,有以下配置:
- 如果配置了localLimit,則設(shè)置本地緩存最大數(shù)量limit的值
- 如果CacheType為BOTH并且配置了localExpire(大于0),則設(shè)置有效時(shí)間expireAfterWrite的值為localExpire,否則如果配置的expire大于0,則設(shè)置其值為expire
- 如果配置了keyConvertor,則根據(jù)該值生成一個(gè)轉(zhuǎn)換函數(shù),沒(méi)有配置的話在初始化構(gòu)造器的時(shí)候根據(jù)全局配置可能已經(jīng)生成了一個(gè)轉(zhuǎn)換函數(shù)(我一般在全局配置中設(shè)置)
- 設(shè)置是否緩存null值
- 1.4. 通過(guò)調(diào)用構(gòu)造器的buildCache()方法構(gòu)建一個(gè)緩存實(shí)例對(duì)象,該方法在之前講到的'CacheBuilder構(gòu)造器'中有分析過(guò)
CacheType為REMOTE則調(diào)用buildRemote方法:
- 1.1. 從GlobalCacheConfig全局配置的remoteCacheBuilders(保存遠(yuǎn)程緩存CacheBuilder構(gòu)造器的集合)中的獲取遠(yuǎn)程緩存該緩存區(qū)域的構(gòu)造器
- 1.2. 克隆一個(gè) CacheBuilder 構(gòu)造器,因?yàn)椴煌彺鎸?shí)例有不同的配置
- 1.3. 將緩存注解的配置信息設(shè)置到構(gòu)造器中,有以下配置:
- 如果配置了expire,則設(shè)置遠(yuǎn)程緩存有效時(shí)間expireAfterWrite的值
- 如果全局設(shè)置遠(yuǎn)程緩存的緩存key的前綴keyPrefix,則設(shè)置緩存key的前綴為"keyPrefix+cacheName",否則我為"cacheName"
- 如果配置了keyConvertor,則根據(jù)該值生成一個(gè)轉(zhuǎn)換函數(shù),沒(méi)有配置的話在初始化構(gòu)造器的時(shí)候根據(jù)全局配置可能已經(jīng)生成了一個(gè)轉(zhuǎn)換函數(shù)(我一般在全局配置中設(shè)置)
- 如果設(shè)置了serialPolicy,則根據(jù)該值生成編碼和解碼函數(shù),沒(méi)有配置的話在初始化構(gòu)造器的時(shí)候根據(jù)全局配置可能已經(jīng)生成了編碼函數(shù)和解碼函數(shù)(我一般在全局配置中設(shè)置)
- 設(shè)置是否緩存null值
- 1.4. 通過(guò)調(diào)用構(gòu)造器的buildCache()方法構(gòu)建一個(gè)緩存實(shí)例對(duì)象
CacheType為BOTH則調(diào)用buildLocal方法構(gòu)建本地緩存實(shí)例,調(diào)用buildRemote方法構(gòu)建遠(yuǎn)程緩存實(shí)例:
1.1. 創(chuàng)建一個(gè)MultiLevelCacheBuilder構(gòu)造器
1.2. 設(shè)置有效時(shí)間為遠(yuǎn)程緩存的有效時(shí)間、添加local和remote緩存實(shí)例、設(shè)置是否單獨(dú)配置了本地緩存的失效時(shí)間(是否有配置localExpire)、設(shè)置是否緩存null值
1.3. 通過(guò)調(diào)用構(gòu)造器的buildCache()方法構(gòu)建一個(gè)緩存實(shí)例對(duì)象
2.設(shè)置刷新策略RefreshPolicy,沒(méi)有的話為null
3.將緩存實(shí)例對(duì)象封裝成CacheHandlerRefreshCache對(duì)象,用于后續(xù)的添加刷新任務(wù),在之前的'AbstractCache抽象類'有講到
4.設(shè)置是否開啟緩存未命中時(shí)加載方法的保護(hù)模式,全局默認(rèn)為false
5.將緩存實(shí)例添加至監(jiān)控管理器中
JetCacheInterceptor
被攔截后的處理在com.alicp.jetcache.anno.aop.JetCacheInterceptor
中,代碼如下:
public class JetCacheInterceptor implements MethodInterceptor, ApplicationContextAware { private static final Logger logger = LoggerFactory.getLogger(JetCacheInterceptor.class); /** * 緩存實(shí)例注解信息 */ @Autowired private ConfigMap cacheConfigMap; /** * Spring 上下文 */ private ApplicationContext applicationContext; /** * 緩存的全局配置 */ private GlobalCacheConfig globalCacheConfig; /** * JetCache 緩存的管理器(包含很多信息) */ ConfigProvider configProvider; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public Object invoke(final MethodInvocation invocation) throws Throwable { if (configProvider == null) { /** * 這里會(huì)獲取到 SpringConfigProvider 可查看 {@link com.alicp.jetcache.autoconfigure.JetCacheAutoConfiguration} */ configProvider = applicationContext.getBean(ConfigProvider.class); } if (configProvider != null && globalCacheConfig == null) { globalCacheConfig = configProvider.getGlobalCacheConfig(); } if (globalCacheConfig == null || !globalCacheConfig.isEnableMethodCache()) { return invocation.proceed(); } // 獲取被攔截的方法 Method method = invocation.getMethod(); // 獲取被攔截的對(duì)象 Object obj = invocation.getThis(); CacheInvokeConfig cac = null; if (obj != null) { // 獲取改方法的Key(方法所在類名+方法名+(參數(shù)類型)+方法返回類型+_被攔截的類名) String key = CachePointcut.getKey(method, obj.getClass()); // 獲取該方法的緩存注解信息,在 Pointcut 中已經(jīng)對(duì)注解進(jìn)行解析并放入 ConfigMap 中 cac = cacheConfigMap.getByMethodInfo(key); } if(logger.isTraceEnabled()){ logger.trace("JetCacheInterceptor invoke. foundJetCacheConfig={}, method={}.{}(), targetClass={}", cac != null, method.getDeclaringClass().getName(), method.getName(), invocation.getThis() == null ? null : invocation.getThis().getClass().getName()); } // 無(wú)緩存相關(guān)注解配置信息表明無(wú)須緩存,直接執(zhí)行該方法 if (cac == null || cac == CacheInvokeConfig.getNoCacheInvokeConfigInstance()) { return invocation.proceed(); } // 為本次調(diào)用創(chuàng)建一個(gè)上下文對(duì)象,包含對(duì)應(yīng)的緩存實(shí)例 CacheInvokeContext context = configProvider.getCacheContext().createCacheInvokeContext(cacheConfigMap); context.setTargetObject(invocation.getThis()); context.setInvoker(invocation::proceed); context.setMethod(method); context.setArgs(invocation.getArguments()); context.setCacheInvokeConfig(cac); context.setHiddenPackages(globalCacheConfig.getHiddenPackages()); // 繼續(xù)往下執(zhí)行 return CacheHandler.invoke(context); } public void setCacheConfigMap(ConfigMap cacheConfigMap) { this.cacheConfigMap = cacheConfigMap; } }
從ConfigMap
中獲取被攔截的方法對(duì)象的緩存配置信息,如果沒(méi)有則直接執(zhí)行該方法,否則繼續(xù)往下執(zhí)行
根據(jù)CacheContext
對(duì)象(SpringCacheContext,因?yàn)樵谥爸v到的'JetCacheAutoConfiguration自動(dòng)配置'中有說(shuō)到注入的是SpringConfigProvider對(duì)象,在其初始化方法中調(diào)用newContext()方法生成SpringCacheContext)調(diào)用其createCacheInvokeContext
方法為本次調(diào)用創(chuàng)建一個(gè)上下文CacheInvokeContext
,并設(shè)置獲取緩存實(shí)例函數(shù),具體實(shí)現(xiàn)邏輯查看上面講到的CacheContext
設(shè)置本次調(diào)用上下文的targetObject為被攔截對(duì)象,invoker為被攔截對(duì)象的調(diào)用器,method為被攔截方法,args為方法入?yún)ⅲ琧acheInvokeConfig為緩存配置信息,hiddenPackages為緩存實(shí)例名稱需要截?cái)嗟陌?/p>
通過(guò)CacheHandler的invoke方法繼續(xù)往下執(zhí)行
CacheHandler
com.alicp.jetcache.anno.method.CacheHandler
用于JetCache處理被攔截的方法,部分代碼如下:
public class CacheHandler implements InvocationHandler { public static Object invoke(CacheInvokeContext context) throws Throwable { if (context.getCacheInvokeConfig().isEnableCacheContext()) { try { CacheContextSupport._enable(); return doInvoke(context); } finally { CacheContextSupport._disable(); } } else { return doInvoke(context); } } private static Object doInvoke(CacheInvokeContext context) throws Throwable { // 獲取緩存實(shí)例配置 CacheInvokeConfig cic = context.getCacheInvokeConfig(); // 獲取注解配置信息 CachedAnnoConfig cachedConfig = cic.getCachedAnnoConfig(); if (cachedConfig != null && (cachedConfig.isEnabled() || CacheContextSupport._isEnabled())) { // 經(jīng)過(guò)緩存中獲取結(jié)果 return invokeWithCached(context); } else if (cic.getInvalidateAnnoConfigs() != null || cic.getUpdateAnnoConfig() != null) { // 根據(jù)結(jié)果刪除或者更新緩存 return invokeWithInvalidateOrUpdate(context); } else { // 執(zhí)行該方法 return invokeOrigin(context); } } private static Object invokeWithCached(CacheInvokeContext context) throws Throwable { // 獲取本地調(diào)用的上下文 CacheInvokeConfig cic = context.getCacheInvokeConfig(); // 獲取注解配置信息 CachedAnnoConfig cac = cic.getCachedAnnoConfig(); // 獲取緩存實(shí)例對(duì)象(不存在則會(huì)創(chuàng)建并設(shè)置到 cac 中) // 可在 JetCacheInterceptor 創(chuàng)建本次調(diào)用的上下文時(shí),調(diào)用 createCacheInvokeContext(cacheConfigMap) 方法中查看詳情 Cache cache = context.getCacheFunction().apply(context, cac); if (cache == null) { logger.error("no cache with name: " + context.getMethod()); // 無(wú)緩存實(shí)例對(duì)象,執(zhí)行原有方法 return invokeOrigin(context); } // 生成緩存 Key 對(duì)象(注解中沒(méi)有配置的話就是入?yún)?,沒(méi)有入?yún)t為 "_$JETCACHE_NULL_KEY$_" ) Object key = ExpressionUtil.evalKey(context, cic.getCachedAnnoConfig()); if (key == null) { // 生成緩存 Key 失敗則執(zhí)行原方法,并記錄 CacheLoadEvent 事件 return loadAndCount(context, cache, key); } /* * 根據(jù)配置的 condition 來(lái)決定是否走緩存 * 緩存注解中沒(méi)有配置 condition 表示所有請(qǐng)求都走緩存 * 配置了 condition 表示滿足條件的才走緩存 */ if (!ExpressionUtil.evalCondition(context, cic.getCachedAnnoConfig())) { // 不滿足 condition 則直接執(zhí)行原方法,并記錄 CacheLoadEvent 事件 return loadAndCount(context, cache, key); } try { // 創(chuàng)建一個(gè)執(zhí)行原有方法的函數(shù) CacheLoader loader = new CacheLoader() { @Override public Object load(Object k) throws Throwable { Object result = invokeOrigin(context); context.setResult(result); return result; } @Override public boolean vetoCacheUpdate() { // 本次執(zhí)行原方法后是否需要更新緩存 return !ExpressionUtil.evalPostCondition(context, cic.getCachedAnnoConfig()); } }; // 獲取結(jié)果 Object result = cache.computeIfAbsent(key, loader); return result; } catch (CacheInvokeException e) { throw e.getCause(); } } private static Object loadAndCount(CacheInvokeContext context, Cache cache, Object key) throws Throwable { long t = System.currentTimeMillis(); Object v = null; boolean success = false; try { // 調(diào)用原有方法 v = invokeOrigin(context); success = true; } finally { t = System.currentTimeMillis() - t; // 發(fā)送 CacheLoadEvent 事件 CacheLoadEvent event = new CacheLoadEvent(cache, t, key, v, success); while (cache instanceof ProxyCache) { cache = ((ProxyCache) cache).getTargetCache(); } if (cache instanceof AbstractCache) { ((AbstractCache) cache).notify(event); } } return v; } private static Object invokeOrigin(CacheInvokeContext context) throws Throwable { // 執(zhí)行被攔截的方法 return context.getInvoker().invoke(); } }
直接查看invokeWithCached
方法:
- 獲取緩存注解信息
- 根據(jù)本地調(diào)用的上下文CacheInvokeContext獲取緩存實(shí)例對(duì)象(調(diào)用其cacheFunction函數(shù)),在CacheContext中有講到
- 如果緩存實(shí)例不存在則直接調(diào)用invokeOrigin方法,執(zhí)行被攔截的對(duì)象的調(diào)用器
- 根據(jù)本次調(diào)用的上下文CacheInvokeContext生成緩存key,根據(jù)配置的緩存key的SpEL表達(dá)式生成,如果沒(méi)有配置則返回入?yún)?duì)象,如果沒(méi)有對(duì)象則返回"_ $JETCACHE_NULL_KEY$_"
- 根據(jù)配置condition表達(dá)式判斷是否需要走緩存
- 創(chuàng)建一個(gè)
CacheLoader
對(duì)象,用于執(zhí)行被攔截的對(duì)象的調(diào)用器,也就是加載原有方法 - 調(diào)用緩存實(shí)例的
computeIfAbsent(key, loader)
方法獲取結(jié)果,這個(gè)方法的處理過(guò)程可查看'緩存API'這一小節(jié)
到此這篇關(guān)于JetCache 緩存框架的使用以及源碼分析的文章就介紹到這了,更多相關(guān)JetCache 緩存框架內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot前后臺(tái)數(shù)據(jù)交互的示例代碼
這篇文章主要介紹了springboot前后臺(tái)數(shù)據(jù)交互的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10@Valid注解的作用及@Valid注解與@Validated的區(qū)別
這篇文章主要介紹了@Valid注解的作用及@Valid注解與@Validated的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08深入學(xué)習(xí)java ThreadLocal的源碼知識(shí)
ThreadLocal是一個(gè)本地線程副本變量工具類。主要用于將私有線程和該線程存放的副本對(duì)象做一個(gè)映射,各個(gè)線程之間的變量互不干擾,特別適用于各個(gè)線程依賴不通的變量值完成操作的場(chǎng)景。下面我們來(lái)詳細(xì)了解一下它吧2019-06-06Java中的UrlDecoder 和 UrlEncoder_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
HTML 格式編碼的實(shí)用工具類。該類包含了將 String 轉(zhuǎn)換為 application/x-www-form-urlencoded MIME 格式的靜態(tài)方法。下文通過(guò)實(shí)例代碼給大家介紹Java中的UrlDecoder 和 UrlEncoder知識(shí),感興趣的的朋友一起看看吧2017-07-07Sentinel 整合SpringCloud的詳細(xì)教程
Spring Cloud Alibaba Sentinel 是阿里巴巴提供的,致力于提供微服務(wù)一站式解決方案,這篇文章主要介紹了Sentinel 之 整合SpringCloud的相關(guān)知識(shí),需要的朋友可以參考下2021-10-10