Spring循環(huán)依賴的解決方法詳解
說明:spring如何解決循環(huán)依賴,是面試中經(jīng)常問到的題目,今天我們就來分享一下spring是如何解決循環(huán)依賴問題的。
什么是循環(huán)依賴:
我們先來看看官方文檔的說法:
通俗來講,就是A依賴B或者B依賴A,或者C依賴自己本身,或是三個(gè)以上,例如A依賴B,B依賴C,C又依賴A。如下圖:
Spring實(shí)例Bean的本質(zhì)
Spring在實(shí)例化一個(gè)bean的時(shí)候,是首先遞歸的實(shí)例化其所依賴的所有bean,直到某個(gè)bean沒有依賴其他bean,此時(shí)就會(huì)將該實(shí)例返回,然后反遞歸的將獲取到的bean設(shè)置為各個(gè)上層bean的屬性的。
循環(huán)依賴主要場(chǎng)景
什么情況下循環(huán)依賴可以被解決
Spring解決循環(huán)依賴是有前置條件的
- 出現(xiàn)循環(huán)依賴的Bean必須要是單例(singleton),如果依賴prototype則完全不會(huì)有此需求。
- 依賴注入的方式不能全是構(gòu)造器注入的方式。
解決方式
Spring是通過三級(jí)緩存來解決上述問題的:
一級(jí)緩存: singletonObjects存儲(chǔ)的是所有創(chuàng)建好了的單例Bean
二級(jí)緩存:earlySingletonObjects完成實(shí)例化,但是還未進(jìn)行屬性注入及初始化的對(duì)象
三級(jí)緩存:singletonFactories提前暴露的一個(gè)單例工廠,二級(jí)緩存中存儲(chǔ)的就是從這個(gè)工廠中獲取到的對(duì)象。
三級(jí)緩存解決循環(huán)依賴流程:
- 獲取A時(shí)首先會(huì)嘗試從一級(jí)緩存singletonObjects中獲??;
- 獲取不到就再?gòu)亩?jí)緩存earlySingletonObjects中獲??;
- 若是還沒有則嘗試從三級(jí)緩存singletonFactories獲??;
- 還是沒有獲取到則再嘗試創(chuàng)建A對(duì)象
- 會(huì)執(zhí)行doGetBean->createBean->createBeanInstance并使用構(gòu)造器實(shí)例化
- 在嘗試給A進(jìn)行初始化時(shí),由于B不存在無法完成初始化,則將半成品A放入第二級(jí)緩存中,進(jìn)入B的創(chuàng)建流程。
- 與先前過程相似,在第三級(jí)緩存中放入beanName和表達(dá)式sharedInstance,進(jìn)入B的初始化過程
- 由于在第二級(jí)緩存中可以找到A,則B可以完成初始化,將成品Bean放入一級(jí)緩存中備用,刪除三級(jí)緩存中的B
- 同時(shí)完成A的初始化,并刪除二級(jí)緩存中的半成品A
具體流程圖如下:
最后我們來個(gè)小小的總結(jié):
Spring通過三級(jí)緩存解決了循環(huán)依賴,其中一級(jí)緩存為單例池(singletonObjects),二級(jí)緩存為早期曝光對(duì)象earlySingletonObjects,三級(jí)緩存為早期曝光對(duì)象工廠(singletonFactories)。
當(dāng)A、B兩個(gè)類發(fā)生循環(huán)引用時(shí),在A完成實(shí)例化后,就使用實(shí)例化后的對(duì)象去創(chuàng)建一個(gè)對(duì)象工廠,添加到三級(jí)緩存中,如果A被AOP代理,那么通過這個(gè)工廠獲取到的就是A代理后的對(duì)象,如果A沒有被AOP代理,那么這個(gè)工廠獲取到的就是A實(shí)例化的對(duì)象。
當(dāng)A進(jìn)行屬性注入時(shí),會(huì)去創(chuàng)建B,同時(shí)B又依賴了A,所以創(chuàng)建B的同時(shí)又會(huì)去調(diào)用getBean(a)來獲取需要的依賴,此時(shí)的getBean(a)會(huì)從緩存中獲取。
- 先獲取到三級(jí)緩存中的工廠;
- 調(diào)用對(duì)象工工廠的getObject方法來獲取到對(duì)應(yīng)的對(duì)象,得到這個(gè)對(duì)象后將其注入到B中。緊接著B會(huì)走完它的生命周期流程,包括初始化、后置處理器等。
- 當(dāng)B創(chuàng)建完后,會(huì)將B再注入到A中,此時(shí)A再完成它的整個(gè)生命周期。至此,循環(huán)依賴結(jié)束!
到此這篇關(guān)于Spring循環(huán)依賴的解決方法詳解的文章就介紹到這了,更多相關(guān)Spring循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java隊(duì)列中Queue與Deque的區(qū)別面試精講
這篇文章主要為大家介紹了java隊(duì)列中Queue與Deque的區(qū)別面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10解決使用@Value(${×××))從properties文件取值的坑
這篇文章主要介紹了解決使用@Value(${×××))從properties文件取值的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07Java在PowerPoint幻燈片中創(chuàng)建散點(diǎn)圖的方法
散點(diǎn)圖是通過兩組數(shù)據(jù)構(gòu)成多個(gè)坐標(biāo)點(diǎn),考察坐標(biāo)點(diǎn)的分布,判斷兩變量之間是否存在某種關(guān)聯(lián)或總結(jié)坐標(biāo)點(diǎn)的分布模式,這篇文章主要介紹了Java如何在PowerPoint幻燈片中創(chuàng)建散點(diǎn)圖,需要的朋友可以參考下2023-04-04Java實(shí)現(xiàn)為Word每一頁設(shè)置不同圖片水印的效果
Word中設(shè)置水印時(shí),可加載圖片設(shè)置為水印效果,但通常添加水印效果時(shí),會(huì)對(duì)所有頁面都設(shè)置成統(tǒng)一效果。所以本文為大家介紹了一個(gè)方法,可以實(shí)現(xiàn)對(duì)每一頁或者某個(gè)頁面設(shè)置不同的水印效果,需要的可以參考一下2022-02-02mybatis升級(jí)mybatis-plus時(shí)踩到的一些坑
這篇文章主要給大家介紹了關(guān)于mybatis升級(jí)mybatis-plus時(shí)踩到的一些坑,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09GateWay動(dòng)態(tài)路由與負(fù)載均衡詳細(xì)介紹
這篇文章主要介紹了GateWay動(dòng)態(tài)路由與負(fù)載均衡,GateWay支持自動(dòng)從注冊(cè)中心中獲取服務(wù)列表并訪問,即所謂的動(dòng)態(tài)路由2022-11-11數(shù)組在java中的擴(kuò)容的實(shí)例方法
在本篇文章里小編給大家分享的是一篇關(guān)于數(shù)組在java中的擴(kuò)容的實(shí)例方法內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-01-01