Spring為什么要用三級(jí)緩存解決循環(huán)依賴呢
1.什么是循環(huán)依賴
本文為了方便說(shuō)明,先設(shè)置兩個(gè)業(yè)務(wù)層對(duì)象,命名為AService和BService。其中Spring是如何把一個(gè)Bean對(duì)象創(chuàng)建出來(lái)的,其生命周期如下:
構(gòu)造方法–> 不同對(duì)象 --> 注入依賴 -->初始化前 --> 初始化后–>放入單例池Map(一級(jí)緩存)—>Bean對(duì)象
Map的數(shù)據(jù)結(jié)構(gòu)為,key表示單例的名稱,而value是一個(gè)對(duì)象。其中單例池就是所謂的一級(jí)緩存。
循環(huán)依賴,如AService中依賴了一個(gè)BService,BService也依賴了AService,AService和BService相互依賴。這里換出現(xiàn)一個(gè)問(wèn)題,但是Spring使用三級(jí)緩存給解決了。
首先我們要看的是為什么出現(xiàn)循環(huán)?
首先在AService中引入了@Componet注解,那AService的生命周期會(huì)交給Spring管理。AService生命周期如下,本文把該例子定義為示例1。
AService生命周期 1.實(shí)例化 --->AService的普通對(duì)象 2.填充BService(添加了AutoWired)-->從單例池中獲取BService(此時(shí)單例池中可能還沒(méi)存放BService的Bean)-->創(chuàng)建BService 當(dāng)單例池中可能還沒(méi)存放BService的Bean時(shí),觸發(fā)創(chuàng)建BService生命周期,步驟和AService一致。如下所示: 2.1.實(shí)例化--->BService的普通對(duì)象 2.2.填充AService(添加了AutoWired)-->單例池(此時(shí)AAService也在創(chuàng)建中,還沒(méi)放入單例池,如果創(chuàng)建的話,就會(huì)出現(xiàn)循環(huán)依賴) 2.3.填充其他屬性 2.4.其他步驟(包括AOP) 2.5.加入到單例池中 3.填充其他屬性 4.其他步驟(包括AOP) 5.加入到單例池中

簡(jiǎn)單解決示例1中的問(wèn)題
主要是定義個(gè)Map存放第一步生成的普通對(duì)象。Map的名稱暫且為boziMap<beanName,普通對(duì)象>,示例2如下所示:
AService生命周期 1.實(shí)例化 --->AService的普通對(duì)象---->存入boziMap<beanName,AService的普通對(duì)象> 2.填充BService(添加了AutoWired)-->從單例池中獲取BService(此時(shí)單例池中可能還沒(méi)存放BService的Bean)-->創(chuàng)建BService 當(dāng)單例池中可能還沒(méi)存放BService的Bean時(shí),觸發(fā)創(chuàng)建BService生命周期,步驟和AService一致。如下所示: 2.1.實(shí)例化--->BService的普通對(duì)象 2.2.填充AService(添加了AutoWired)-->單例池(此時(shí)AAService也在創(chuàng)建中,還沒(méi)放入單例池,如果創(chuàng)建的話,就會(huì)出現(xiàn)循環(huán)依賴)--->boziMap中取AService對(duì)象。 2.3.填充其他屬性 2.4.其他步驟(包括AOP) 2.5.加入到單例池中 3.填充其他屬性 4.其他步驟(包括AOP)--> AService代理對(duì)象(AServiceProxy) 5.加入到單例池中
其中AService對(duì)象的代理對(duì)象和AService的代理對(duì)象如下所示:

示例2中通過(guò)一個(gè)boziMap打破了循環(huán)創(chuàng)建bean對(duì)象,而產(chǎn)生的循環(huán)依賴。而在AOP過(guò)程中,步驟2.2.填充AService時(shí),應(yīng)該是把4.其他步驟,AService代理對(duì)象賦值給步驟2.2.填充AService的普通對(duì)象。所以要對(duì)示例2進(jìn)行優(yōu)化,把AOP放到第二步,先判斷是否出現(xiàn)循環(huán)依賴,在進(jìn)行AOP,再把AService的代理對(duì)象放入biziMap中。得到示例3:
AService生命周期 0.creatingSet[AService]表示正在創(chuàng)建中的Bean。 1.實(shí)例化 --->AService的普通對(duì)象 2.填充BService(添加了AutoWired)-->從單例池中獲取BService (此時(shí)單例池中可能還沒(méi)存放BService的Bean)-->創(chuàng)建BService 當(dāng)單例池中可能還沒(méi)存放BService的Bean時(shí),觸發(fā)創(chuàng)建BService生命周期,步驟和AService一致。如下所示: 2.1.實(shí)例化--->BService的普通對(duì)象 2.2.填充aService(添加了AutoWired)-->單例池-->creatingSet中是否存在AService-->AOP-->AService的代理對(duì)象-->最后賦值給BService中的屬性aService 2.3.填充其他屬性 2.4.其他步驟 2.5.加入到單例池中 3.填充其他屬性 4.其他步驟(包括AOP)--> AService代理對(duì)象(AServiceProxy) 5.加入到單例池中
此時(shí)加大難度,多加一個(gè)CService,C中依賴的A,而A中也依賴了C,根據(jù)示例3,會(huì)得到如下步驟。把本例子定義為示例4,該例子中,填充bService和cService會(huì)反復(fù)創(chuàng)建代理對(duì)象。
AService生命周期 0.creatingSet[AService]表示正在創(chuàng)建中的Bean。 1.實(shí)例化 --->AService的普通對(duì)象 2.填充BService,CService(添加了AutoWired)-->從單例池中獲取BService (此時(shí)單例池中可能還沒(méi)存放BService的Bean)-->創(chuàng)建BService 當(dāng)單例池中可能還沒(méi)存放BService的Bean時(shí),觸發(fā)創(chuàng)建BService生命周期,步驟和AService一致。如下所示: 2.1.實(shí)例化--->BService的普通對(duì)象 2.2.填充aService(添加了AutoWired)-->單例池-->creatingSet中是否存在AService-->AOP-->AService的代理對(duì)象-->最后賦值給BService中的屬性aService 2.3.填充其他屬性 2.4.其他步驟 2.5.加入到單例池中 填充CService,CService的生命周期 2.1.實(shí)例化-->BService的普通對(duì)象 2.2.填充aService(添加了AutoWired)-->creatingSet中是否存在AService-->AOP-->AService的代理對(duì)象-->最后賦值給CService中的屬性aService 2.3.填充其他屬性 2.4.其他步驟 2.5.加入到單例池中 3.填充其他屬性 4.其他步驟(包括AOP)--> AService代理對(duì)象(AServiceProxy) 5.加入到單例池中
為了解決這個(gè)問(wèn)題,使用二級(jí)緩存存放A的代理對(duì)象。
AService生命周期 0.creatingSet[AService]表示正在創(chuàng)建中的Bean。 1.實(shí)例化 --->AService的普通對(duì)象 2.填充BService,CService(添加了AutoWired)-->從單例池中獲取BService (此時(shí)單例池中可能還沒(méi)存放BService的Bean)-->創(chuàng)建BService 當(dāng)單例池中可能還沒(méi)存放BService的Bean時(shí),觸發(fā)創(chuàng)建BService生命周期,步驟和AService一致。如下所示: 2.1.實(shí)例化--->BService的普通對(duì)象 2.2.填充aService(添加了AutoWired)-->單例池-->creatingSet中是否存在AService-->循環(huán)依賴 --->earlySingletonObjects是否存在aService的代理對(duì)象,存在返回bean即可 -->AOP-->AService的代理對(duì)象-->存入二級(jí)緩存,earlySingletonObjects<beanName,AService的代理對(duì)象>-->最后賦值給BService中的屬性aService 2.3.填充其他屬性 2.4.其他步驟 2.5.加入到單例池中 填充CService,CService的生命周期 2.1.實(shí)例化-->BService的普通對(duì)象 2.2.填充aService(添加了AutoWired)-->單例池-->creatingSet中是否存在AService-->earlySingletonObjects 2.3.填充其他屬性 2.4.其他步驟 2.5.加入到單例池中 3.填充其他屬性 4.其他步驟(包括AOP)--> AService代理對(duì)象(AServiceProxy) 5.加入到單例池中
三級(jí)緩存
三級(jí)緩存為了打破循環(huán),在第一步驟中生成的不同對(duì)象,用三級(jí)緩存保存起來(lái)。三級(jí)緩存在Spring‘源碼中可singletonFactories,是一個(gè)Map,其中它的value存的是一個(gè)lambda表達(dá)式,其中的邏輯是,判斷是否需要AOP,需要?jiǎng)t返回一個(gè)代理對(duì)象,反之則返回Service的普通對(duì)象。’

小結(jié)
一級(jí)緩存的作用就是保存經(jīng)過(guò)完整生命周期的Bean對(duì)象。
二級(jí)緩存,早期由于出現(xiàn)循環(huán)依賴,保存那些還沒(méi)完整走完bean生命周期的bean對(duì)象,是為了給其他Bean填充屬性時(shí)使用的代理對(duì)象賦值。
三級(jí)緩存,在出現(xiàn)循環(huán)依賴時(shí),如果二級(jí)緩存中沒(méi)有存有對(duì)應(yīng)的bean對(duì)象,需要通過(guò)三級(jí)緩存去判斷,是否需要AOP,是則需要返回代理對(duì)象,否則需要返回普通對(duì)象。三級(jí)返回的的結(jié)果最終還是存在二級(jí)緩存中。Spring的核心源碼實(shí)現(xiàn)如下所示。

到此這篇關(guān)于Spring為什么要用三級(jí)緩存解決循環(huán)依賴的文章就介紹到這了,更多相關(guān)Spring三級(jí)緩存解決循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決SpringBoot在后臺(tái)接收前臺(tái)傳遞對(duì)象方式的問(wèn)題
這篇文章主要介紹了解決SpringBoot在后臺(tái)接收前臺(tái)傳遞對(duì)象方式的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
IntelliJ IDEA 配置與插件全部遷移到其他盤(pán)徹底釋放C盤(pán)空間
這篇文章給大家介紹IntelliJ IDEA 配置與插件全部遷移到其他盤(pán)徹底釋放C盤(pán)空間,本文將詳細(xì)解釋這一問(wèn)題的根源,并提供兩種實(shí)用的遷移方案,幫你把 IDEA 的所有用戶數(shù)據(jù)安全地移到其他盤(pán),徹底釋放 C 盤(pán)空間,感興趣的朋友跟隨小編一起看看吧2026-03-03
SpringBoot部署在tomcat容器中運(yùn)行的部署方法
這篇文章主要介紹了SpringBoot部署在tomcat容器中運(yùn)行的部署方法,需要的朋友可以參考下2018-10-10
使用RestTemplate 調(diào)用遠(yuǎn)程接口上傳文件方式
這篇文章主要介紹了使用RestTemplate 調(diào)用遠(yuǎn)程接口上傳文件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
實(shí)例解析觀察者模式及其在Java設(shè)計(jì)模式開(kāi)發(fā)中的運(yùn)用
觀察者模式定義了一種一對(duì)多的依賴關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽(tīng)某一個(gè)主題對(duì)象,這個(gè)主題對(duì)象在狀態(tài)上發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象,使它們能夠自動(dòng)更新自己.下面就以實(shí)例解析觀察者模式及其在Java設(shè)計(jì)模式開(kāi)發(fā)中的運(yùn)用2016-05-05
java?讀寫(xiě)鎖的使用及它的優(yōu)點(diǎn)
這篇文章主要介紹了java?讀寫(xiě)鎖的使用及它的優(yōu)點(diǎn),讀寫(xiě)鎖的特點(diǎn)就是是讀讀不互斥、讀寫(xiě)互斥、寫(xiě)寫(xiě)互斥,下面具體使用分享需要的小伙伴可以參考一下2022-05-05

