JDK動(dòng)態(tài)代理之WeakCache緩存的實(shí)現(xiàn)機(jī)制
上一篇我們分析了Proxy類的內(nèi)部是怎樣產(chǎn)生代理類的,我們看到了Proxy內(nèi)部用到了緩存機(jī)制,如果根據(jù)提供的類加載器和接口數(shù)組能在緩存中找到代理類就直接返回該代理類,否則會(huì)調(diào)用ProxyClassFactory工廠去生成代理類。這里用到的緩存是二級(jí)緩存,它的一級(jí)緩存key是根據(jù)類加載器生成的,二級(jí)緩存key是根據(jù)接口數(shù)組生成的。具體的內(nèi)部機(jī)制我們直接貼上代碼詳細(xì)解釋。
//Reference引用隊(duì)列 private final ReferenceQueue<K> refQueue = new ReferenceQueue<>(); //緩存的底層實(shí)現(xiàn), key為一級(jí)緩存, value為二級(jí)緩存。 為了支持null, map的key類型設(shè)置為Object private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>(); //reverseMap記錄了所有代理類生成器是否可用, 這是為了實(shí)現(xiàn)緩存的過期機(jī)制 private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>(); //生成二級(jí)緩存key的工廠, 這里傳入的是KeyFactory private final BiFunction<K, P, ?> subKeyFactory; //生成二級(jí)緩存value的工廠, 這里傳入的是ProxyClassFactory private final BiFunction<K, P, V> valueFactory; //構(gòu)造器, 傳入生成二級(jí)緩存key的工廠和生成二級(jí)緩存value的工廠 public WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory) { this.subKeyFactory = Objects.requireNonNull(subKeyFactory); this.valueFactory = Objects.requireNonNull(valueFactory); }
首先我們看一下WeakCache的成員變量和構(gòu)造器,WeakCache緩存的內(nèi)部實(shí)現(xiàn)是通過ConcurrentMap來完成的,成員變量map就是二級(jí)緩存的底層實(shí)現(xiàn),reverseMap是為了實(shí)現(xiàn)緩存的過期機(jī)制,subKeyFactory是二級(jí)緩存key的生成工廠,通過構(gòu)造器傳入,這里傳入的值是Proxy類的KeyFactory,valueFactory是二級(jí)緩存value的生成工廠,通過構(gòu)造器傳入,這里傳入的是Proxy類的ProxyClassFactory。接下來我們看一下WeakCache的get方法。
public V get(K key, P parameter) { //這里要求實(shí)現(xiàn)的接口不能為空 Objects.requireNonNull(parameter); //清除過期的緩存 expungeStaleEntries(); //將ClassLoader包裝成CacheKey, 作為一級(jí)緩存的key Object cacheKey = CacheKey.valueOf(key, refQueue); //獲取得到二級(jí)緩存 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); //如果根據(jù)ClassLoader沒有獲取到對(duì)應(yīng)的值 if (valuesMap == null) { //以CAS方式放入, 如果不存在則放入,否則返回原先的值 ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); //如果oldValuesMap有值, 說明放入失敗 if (oldValuesMap != null) { valuesMap = oldValuesMap; } } //根據(jù)代理類實(shí)現(xiàn)的接口數(shù)組來生成二級(jí)緩存key, 分為key0, key1, key2, keyx Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); //這里通過subKey獲取到二級(jí)緩存的值 Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; //這個(gè)循環(huán)提供了輪詢機(jī)制, 如果條件為假就繼續(xù)重試直到條件為真為止 while (true) { //如果通過subKey取出來的值不為空 if (supplier != null) { //在這里supplier可能是一個(gè)Factory也可能會(huì)是一個(gè)CacheValue //在這里不作判斷, 而是在Supplier實(shí)現(xiàn)類的get方法里面進(jìn)行驗(yàn)證 V value = supplier.get(); if (value != null) { return value; } } if (factory == null) { //新建一個(gè)Factory實(shí)例作為subKey對(duì)應(yīng)的值 factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { //到這里表明subKey沒有對(duì)應(yīng)的值, 就將factory作為subKey的值放入 supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { //到這里表明成功將factory放入緩存 supplier = factory; } //否則, 可能期間有其他線程修改了值, 那么就不再繼續(xù)給subKey賦值, 而是取出來直接用 } else { //期間可能其他線程修改了值, 那么就將原先的值替換 if (valuesMap.replace(subKey, supplier, factory)) { //成功將factory替換成新的值 supplier = factory; } else { //替換失敗, 繼續(xù)使用原先的值 supplier = valuesMap.get(subKey); } } } }
WeakCache的get方法并沒有用鎖進(jìn)行同步,那它是怎樣實(shí)現(xiàn)線程安全的呢?因?yàn)樗乃袝?huì)進(jìn)行修改的成員變量都使用了ConcurrentMap,這個(gè)類是線程安全的。因此它將自身的線程安全委托給了ConcurrentMap, get方法盡可能的將同步代碼塊縮小,這樣可以有效提高WeakCache的性能。我們看到ClassLoader作為了一級(jí)緩存的key,這樣可以首先根據(jù)ClassLoader篩選一遍,因?yàn)椴煌珻lassLoader加載的類是不同的。然后它用接口數(shù)組來生成二級(jí)緩存的key,這里它進(jìn)行了一些優(yōu)化,因?yàn)榇蟛糠诸惗际菍?shí)現(xiàn)了一個(gè)或兩個(gè)接口,所以二級(jí)緩存key分為key0,key1,key2,keyX。key0到key2分別表示實(shí)現(xiàn)了0到2個(gè)接口,keyX表示實(shí)現(xiàn)了3個(gè)或以上的接口,事實(shí)上大部分都只會(huì)用到key1和key2。這些key的生成工廠是在Proxy類中,通過WeakCache的構(gòu)造器將key工廠傳入。這里的二級(jí)緩存的值是一個(gè)Factory實(shí)例,最終代理類的值是通過Factory這個(gè)工廠來獲得的。
private final class Factory implements Supplier<V> { //一級(jí)緩存key, 根據(jù)ClassLoader生成 private final K key; //代理類實(shí)現(xiàn)的接口數(shù)組 private final P parameter; //二級(jí)緩存key, 根據(jù)接口數(shù)組生成 private final Object subKey; //二級(jí)緩存 private final ConcurrentMap<Object, Supplier<V>> valuesMap; Factory(K key, P parameter, Object subKey, ConcurrentMap<Object, Supplier<V>> valuesMap) { this.key = key; this.parameter = parameter; this.subKey = subKey; this.valuesMap = valuesMap; } @Override public synchronized V get() { //這里再一次去二級(jí)緩存里面獲取Supplier, 用來驗(yàn)證是否是Factory本身 Supplier<V> supplier = valuesMap.get(subKey); if (supplier != this) { //在這里驗(yàn)證supplier是否是Factory實(shí)例本身, 如果不則返回null讓調(diào)用者繼續(xù)輪詢重試 //期間supplier可能替換成了CacheValue, 或者由于生成代理類失敗被從二級(jí)緩存中移除了 return null; } V value = null; try { //委托valueFactory去生成代理類, 這里會(huì)通過傳入的ProxyClassFactory去生成代理類 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { //如果生成代理類失敗, 就將這個(gè)二級(jí)緩存刪除 if (value == null) { valuesMap.remove(subKey, this); } } //只有value的值不為空才能到達(dá)這里 assert value != null; //使用弱引用包裝生成的代理類 CacheValue<V> cacheValue = new CacheValue<>(value); //將包裝后的cacheValue放入二級(jí)緩存中, 這個(gè)操作必須成功, 否則就報(bào)錯(cuò) if (valuesMap.replace(subKey, this, cacheValue)) { //將cacheValue成功放入二級(jí)緩存后, 再對(duì)它進(jìn)行標(biāo)記 reverseMap.put(cacheValue, Boolean.TRUE); } else { throw new AssertionError("Should not reach here"); } //最后返回沒有被弱引用包裝的代理類 return value; } }
我們再看看Factory這個(gè)內(nèi)部工廠類,可以看到它的get方法是使用synchronized關(guān)鍵字進(jìn)行了同步。進(jìn)行g(shù)et方法后首先會(huì)去驗(yàn)證subKey對(duì)應(yīng)的suppiler是否是工廠本身,如果不是就返回null,而WeakCache的get方法會(huì)繼續(xù)進(jìn)行重試。如果確實(shí)是工廠本身,那么就會(huì)委托ProxyClassFactory生成代理類,ProxyClassFactory是在構(gòu)造WeakCache的時(shí)候傳入的。所以這里解釋了為什么最后會(huì)調(diào)用到Proxy的ProxyClassFactory這個(gè)內(nèi)部工廠來生成代理類。生成代理類后使用弱引用進(jìn)行包裝并放入reverseMap中,最后會(huì)返回原裝的代理類。
至此已經(jīng)為大家詳細(xì)揭示了WeakCache緩存的實(shí)現(xiàn)包括它的一級(jí)緩存和二級(jí)緩存實(shí)現(xiàn)的原理,以及二級(jí)緩存key生成的原理,還有最后它是怎樣調(diào)用ProxyClassFactory來生成代理類的。在下一篇中將會(huì)深入ProxyGenerator這個(gè)類,來看看具體的代理類的字節(jié)碼生成過程。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- java動(dòng)態(tài)代理(jdk與cglib)詳細(xì)解析
- java jdk動(dòng)態(tài)代理詳解
- java代理 jdk動(dòng)態(tài)代理應(yīng)用案列
- 深入理解java動(dòng)態(tài)代理的兩種實(shí)現(xiàn)方式(JDK/Cglib)
- 淺談Java代理(jdk靜態(tài)代理、動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理)
- Java JDK動(dòng)態(tài)代理的基本原理詳細(xì)介紹
- Java JDK 動(dòng)態(tài)代理的使用方法示例
- Spring學(xué)習(xí)之動(dòng)態(tài)代理(JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理)
- Java JDK動(dòng)態(tài)代理(AOP)的實(shí)現(xiàn)原理與使用詳析
- 詳解java JDK 動(dòng)態(tài)代理類分析(java.lang.reflect.Proxy)
相關(guān)文章
SpringSecurity登錄使用JSON格式數(shù)據(jù)的方法
這篇文章主要介紹了SpringSecurity登錄使用JSON格式數(shù)據(jù)的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02基于JavaMail實(shí)現(xiàn)簡單郵件發(fā)送
這篇文章主要為大家詳細(xì)介紹了基于JavaMail實(shí)現(xiàn)簡單郵件發(fā)送,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08Spring?代理?Bean?獲取不到原始?Bean?對(duì)象注解解決方法
這篇文章主要介紹了Spring?代理?Bean?獲取不到原始?Bean?對(duì)象注解解決方法,文章圍繞主題相關(guān)資料展開詳細(xì)介紹,需要的小伙伴可以參考一下2022-04-04Java多線程下的其他組件之CyclicBarrier、Callable、Future和FutureTask詳解
這篇文章主要介紹了Java多線程下的其他組件之CyclicBarrier、Callable、Future和FutureTask詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07SpringBoot3.0集成MybatisPlus的實(shí)現(xiàn)方法
本文主要介紹了SpringBoot3.0集成MybatisPlus的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08Java中java.lang.ClassCastException異常原因以及解決方法詳解
這篇文章主要給大家介紹了關(guān)于Java中java.lang.ClassCastException異常原因以及解決方法的相關(guān)資料,ClassCastException從字面上看是類型轉(zhuǎn)換錯(cuò)誤,通常是進(jìn)行強(qiáng)制類型轉(zhuǎn)換時(shí)候出的錯(cuò)誤,需要的朋友可以參考下2024-02-02Spring Boot熱加載jar實(shí)現(xiàn)動(dòng)態(tài)插件的思路
本文主要介紹在 Spring Boot 工程中熱加載 jar 包并注冊成為 Bean 對(duì)象的一種實(shí)現(xiàn)思路,在動(dòng)態(tài)擴(kuò)展功能的同時(shí)支持在插件中注入主程序的 Bean 實(shí)現(xiàn)功能更強(qiáng)大的插件2021-10-10