關(guān)于spring三級(jí)緩存的解讀
spring三級(jí)緩存的解讀
spring 中為了解決 B的重復(fù)利用,A 依賴B 的循環(huán)依賴,aop 問(wèn)題,多線程可能拿到不完整的bean 的問(wèn)題引入了3層緩存,分別是
/** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
singletonObjects
用于存放單例bean 實(shí)例earlySingletonObjects
用于存放早期的bean 實(shí)例singletonFactories
用于存放簡(jiǎn)單工廠實(shí)例
從3級(jí)緩存中 獲取bean 的方法有如下兩個(gè)
1.從緩存中獲取,主要用來(lái)解決循環(huán)依賴問(wèn)題
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 這里不用考慮指令重排,因?yàn)?bean 初始化完成后才會(huì)放入map Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 加鎖,與另一個(gè)getSingleton 方法 互斥訪問(wèn),保證并發(fā)情況下獲取到不完整的bean synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 獲取工廠bean ,因?yàn)榭赡苁莂op 生成的代理,這里二級(jí)和三級(jí)緩存保證獲取到的bean 是最后的完整bean ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); // 放入 二級(jí)緩存中,從3級(jí)緩存中移除 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
2.創(chuàng)建bean 的真正邏輯
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { // 再次從一級(jí)緩存中獲取,因?yàn)槎嗑€程加載同一個(gè)bean 時(shí), //相當(dāng)于 雙重檢查 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } // 標(biāo)記bean 正在創(chuàng)建 beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } if (newSingleton) { addSingleton(beanName, singletonObject); } } return singletonObject; } }
我們先假設(shè)不存在aop 與多線程競(jìng)爭(zhēng)的情況,此時(shí)我們只需要一級(jí)緩存就可以解決循環(huán)依賴的問(wèn)題
- 1.構(gòu)造bean A ->
- 2.把A 放入 map1 ->
- 3. 填充A的屬性 ->
- 4.創(chuàng)建bean B ->
- 5.填充B->
- 6.從緩存中拿到A->
- 7.返回B ->
- 8返回A
即不存在aop且不考慮多線程的情況下,只要一個(gè)map1 我們就能解決循環(huán)依賴的問(wèn)題
3.現(xiàn)在我們考慮aop
假設(shè)A 有屬性B ,B 有屬性A ,且我們對(duì)A 做了aop,在創(chuàng)建B 的時(shí)候我們希望拿到的是A 的aop 代理對(duì)象,此時(shí)假設(shè)填充B 的時(shí)候獲取A ,
假設(shè)如果我們用一層緩存解決?
可以對(duì)是否aop 代理過(guò)存儲(chǔ)一個(gè)標(biāo)志位,此時(shí)必然在后面獲取bean 的時(shí)候都要多一層判斷,所以此時(shí)需要二層緩存來(lái)解決
一層存放aop 代理過(guò)的對(duì)象或不需要aop 的對(duì)象,一層存放非aop 對(duì)象,即創(chuàng)建工廠。
4.我們?cè)倏紤]并發(fā)的情況,即多線程獲取一個(gè)單例
這里如果熟悉單例模式的創(chuàng)建過(guò)程,就不難理解第二個(gè)方法中的synchronized 與加鎖后再次嘗試從一級(jí)緩存中獲取,把第一個(gè)方法可以看作第一次非空判斷,那么就是雙重鎖檢查保證單例。但是只做了雙重鎖檢查保證單例可以支持多線程操作嗎?
答案是否定的,假設(shè)我們?yōu)榱私鉀Qaop 使用了二層緩存,對(duì)于A 來(lái)說(shuō) 并沒(méi)有完全完成賦值和初始化的操作,但是由于A 依賴B ,B依賴于A的aop 代理對(duì)象,此時(shí)A 已經(jīng)在一級(jí)中了,那么線程2邊能從map1 中獲取到 對(duì)象A,(當(dāng)然你也可以對(duì)整個(gè)創(chuàng)建過(guò)程加鎖,那么就不存在多線程問(wèn)題)。但是此時(shí)A 對(duì)象并沒(méi)有完成賦值與init 的方法執(zhí)行,這樣很容易造成線程2 的異常,所以還需要一層緩存將完成整個(gè)創(chuàng)建過(guò)程的bean ,與沒(méi)有創(chuàng)建完成的純潔bean 隔離開(kāi),所以才有了3級(jí)緩存。
此時(shí)再考慮3級(jí)緩存的獲取,基于并發(fā)的考慮就不難理解第一個(gè)方法為何要synchronized 了,即保證并發(fā)下,緩存只在一層緩存中存在。
綜上來(lái)說(shuō),我的理解,aop 問(wèn)題需要兩層緩存來(lái)解決,而考慮多線程并發(fā)的情況下,所以需要三層緩存來(lái)解決。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
JavaScript中的isTrusted屬性及其應(yīng)用場(chǎng)景詳解
在現(xiàn)代 Web 開(kāi)發(fā)中,JavaScript 是構(gòu)建交互式應(yīng)用的核心語(yǔ)言,隨著前端技術(shù)的不斷發(fā)展,開(kāi)發(fā)者需要處理越來(lái)越多的復(fù)雜場(chǎng)景,例如事件處理、數(shù)據(jù)傳遞和狀態(tài)管理等,本文將通過(guò)一個(gè)實(shí)際案例,深入探討 isTrusted 屬性的來(lái)源、作用,需要的朋友可以參考下2025-01-01java中l(wèi)ong和Long有什么區(qū)別詳解
這篇文章主要介紹了Java中l(wèi)ong和Long是基本數(shù)據(jù)類型和包裝數(shù)據(jù)類型的區(qū)別,包括默認(rèn)值、內(nèi)存占用、使用場(chǎng)景、方法支持以及裝箱和拆箱,包裝數(shù)據(jù)類型如Integer提供了許多有用的方法,需要的朋友可以參考下2025-02-02SpringBoot創(chuàng)建自定義starter詳解
這篇文章主要介紹了SpringBoot創(chuàng)建自定義starter詳解,Starter是Spring Boot中的一個(gè)非常重要的概念,Starter相當(dāng)于模塊,它能將模塊所需的依賴整合起來(lái)并對(duì)模塊內(nèi)的Bean根據(jù)環(huán)境(條件)進(jìn)行自動(dòng)配置,需要的朋友可以參考下2024-01-01徹底解決Spring mvc中時(shí)間的轉(zhuǎn)換和序列化等問(wèn)題
這篇文章主要介紹了徹底解決Spring mvc中時(shí)間的轉(zhuǎn)換和序列化等問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09springboot2.1.3配置sftp自定義sftp連接池的詳細(xì)過(guò)程
這篇文章主要介紹了springboot2.1.3配置sftp自定義sftp連接池的詳細(xì)過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08java實(shí)現(xiàn)操作系統(tǒng)的短進(jìn)程作業(yè)調(diào)度示例分享
java編寫(xiě)的實(shí)現(xiàn)了操作系統(tǒng)中的短作業(yè)進(jìn)程,可以實(shí)現(xiàn)幾道作業(yè)同時(shí)作業(yè)調(diào)度2014-02-02