spring如何解決循環(huán)依賴的

1、開篇

本節(jié)課會(huì)聊聊spring IOC如何解決循環(huán)依賴問(wèn)題。包括如下內(nèi)容:

什么是循環(huán)依賴

Spring IoC處理循環(huán)依賴的思路

處理循環(huán)依賴舉例

2、什么是循環(huán)依賴

Spring IoC中的循環(huán)依賴其實(shí)就是循環(huán)引用,兩個(gè)或者兩個(gè)以上的 Bean 互相持有對(duì)方,最終形成閉環(huán)。如圖1所示,如A依賴于B,B依賴于C,C又依賴于A。這樣這樣一個(gè)場(chǎng)景,初始化A的時(shí)候需要完成B的初始化,而完成B的初始化又需要完成C的初始化,最后C又依賴于A,如此這般A永遠(yuǎn)也無(wú)法完成初始化的操作。這種對(duì)象的相互依賴形成閉環(huán)的關(guān)系被稱作循環(huán)依賴。


? ? ? ? 圖1 循環(huán)依賴

在Spring IoC的使用場(chǎng)景中有兩類循環(huán)依賴是無(wú)解的:

構(gòu)造器的循環(huán)依賴:構(gòu)造器要調(diào)用構(gòu)造函數(shù)new 一個(gè)對(duì)象出來(lái),而參數(shù)又依賴于另一個(gè)對(duì)象。創(chuàng)建類A依賴于類B,new 的時(shí)候去創(chuàng)建類B發(fā)現(xiàn)類B不存在就會(huì)出錯(cuò)拋出 BeanCurrentlyInCreationException 異常。

prototype 原型bean循環(huán)依賴:原型bean的初始化過(guò)程中不論是通過(guò)構(gòu)造器參數(shù)循環(huán)依賴還是通過(guò)set方法產(chǎn)生的循環(huán)依賴也會(huì)拋出異常。

然而針對(duì) singleton bean的循環(huán)依賴的場(chǎng)景可以通過(guò)三級(jí)緩存的方式解決。下面就根據(jù)該解決方案展開說(shuō)明。

3、Spring IoC處理循環(huán)依賴的思路

在整理Spring IoC處理singleton bean循環(huán)依賴的思路之前先來(lái)復(fù)習(xí)一下bean的生命周期,其包括的三個(gè)步驟:

實(shí)例化:執(zhí)行了bean的構(gòu)造方法,bean中依賴的對(duì)象還未賦值

設(shè)置屬性:給bean中依賴的對(duì)象賦值,若被依賴的對(duì)象尚未初始化,則先進(jìn)行該對(duì)象的生命周期(遞歸)。

初始化:執(zhí)行bean的初始化方法,回調(diào)方法等。

解決循環(huán)依賴的思路就藏在這三個(gè)步驟中,在實(shí)例化與設(shè)置屬性兩個(gè)步驟之間引入緩存機(jī)制,將已經(jīng)創(chuàng)建好實(shí)例但是并沒有設(shè)置屬性的bean放到緩存里,緩存中是沒有屬性設(shè)置的實(shí)例對(duì)象。假設(shè)A對(duì)象和B對(duì)象互相依賴,A對(duì)象的創(chuàng)建需要引用到B對(duì)象,而B對(duì)象的創(chuàng)建也需要A對(duì)象。在創(chuàng)建A對(duì)象的時(shí)候可以將其放入到緩存中,當(dāng)B對(duì)象創(chuàng)建的時(shí)候直接從緩存里引用A對(duì)象(此時(shí)的A對(duì)象只完成了實(shí)例化,沒有進(jìn)行設(shè)置屬性的操作,因此不是完成的A對(duì)象,我們稱之為半成品A對(duì)象),當(dāng)B對(duì)象利用這個(gè)半成品的A對(duì)象完成實(shí)例創(chuàng)建以后(三個(gè)步驟都完成),再被A對(duì)象引用進(jìn)去,則A對(duì)象也完成了創(chuàng)建。

上文提到的緩存在這里做一個(gè)解釋,我們將其分為三級(jí),每級(jí)緩存都起到不同的作用,如下表格所示:

一級(jí)緩存:用于存放完全初始化好的 bean,也就是完成三個(gè)步驟的bean,拿出來(lái)的bean是可以直接使用的。

二級(jí)緩存:存放原始的 bean 對(duì)象,此時(shí)的對(duì)象只進(jìn)行了實(shí)例化但是沒有填充屬性,也就是我們所說(shuō)的“半成品對(duì)象”,它的建立是用來(lái)解決循環(huán)依賴的。

三級(jí)緩存:用來(lái)存放 bean 工廠對(duì)象,這個(gè)工廠對(duì)象是用來(lái)產(chǎn)生bean對(duì)象的實(shí)例的。


解決循環(huán)依賴的整個(gè)過(guò)程是:

先從一級(jí)緩存里取bean實(shí)例,如果沒有對(duì)應(yīng)的bean實(shí)例,二級(jí)緩存里取,如果二級(jí)緩存中也沒有bean實(shí)例,singletonFactories三級(jí)緩存里獲取。由于三級(jí)緩存存放著產(chǎn)生bean實(shí)例的工廠類,因此可以通過(guò)該工廠類產(chǎn)生bean實(shí)例。

這里可以調(diào)用工廠類暴露的getObject方法返回早期暴露對(duì)象引用,也是我們所說(shuō)的半成品bean,也可以成為earlySingletonObject。并且將這個(gè)半成品bean放到二級(jí)緩存里,在三級(jí)緩存里刪除該bean。什么時(shí)候這個(gè)半成品填充了屬性以后,就被移動(dòng)到一級(jí)緩存中,也就是被作為可以使用的已經(jīng)完成初始化的實(shí)例bean了,處理循環(huán)依賴的過(guò)程宣告完畢。下面通過(guò)一個(gè)例子讓大家更好理解這個(gè)思路。

4、處理循環(huán)依賴舉例

根據(jù)上面的思路,這里假設(shè)A 和 B 互相依賴,如圖2所示,在A 創(chuàng)建實(shí)例的時(shí)候使用了getBean方法,通過(guò)createBeanInstatnce方法對(duì)A進(jìn)行實(shí)例化。此時(shí)的A只是被實(shí)例化出來(lái)了,并沒有進(jìn)行填充屬性的操作,然后通過(guò)addSingletonFactory的方法將創(chuàng)建A的工廠類添加到三級(jí)緩存中。上面的思路中提到了這個(gè)放到三級(jí)緩存中的工廠類是用來(lái)生成bean實(shí)例用的。

接著往下,當(dāng)通過(guò)populateBean填充實(shí)例A屬性的時(shí)候發(fā)現(xiàn),A依賴B。此時(shí)開始通過(guò)getBean方法創(chuàng)建B的實(shí)例,依舊通過(guò)createBeanInstatnce方法對(duì)B進(jìn)行實(shí)例化,也把創(chuàng)建B實(shí)例的工廠類通過(guò)addSingletonFactory方法添加到三級(jí)緩存中。在使用populateBean方法填充B的屬性時(shí),發(fā)現(xiàn)B依賴A,此時(shí)通過(guò)getBean方法對(duì)A進(jìn)行實(shí)例化。

這個(gè)時(shí)候就出現(xiàn)循環(huán)依賴的情況了,getBean方法先從一級(jí)緩存中獲取 A 的實(shí)例,發(fā)現(xiàn)沒有,再去二級(jí)緩存中找,還是找不到,沒有辦法只有找三級(jí)緩存中的A 實(shí)例創(chuàng)建工廠去創(chuàng)建A的實(shí)例。在前面的步驟中A 已經(jīng)將工廠類通過(guò)addSingletonFactory方法存放到了三級(jí)緩存中,于是調(diào)用A的工廠類創(chuàng)造A的實(shí)例,并且將其放到二級(jí)緩存中返回給B 用來(lái)填充B的屬性,當(dāng)B完成屬性填充以后產(chǎn)生了B的實(shí)例,返回給populateBean(A)使用,此時(shí)A獲取了B的實(shí)例(完成屬性填充的B實(shí)例)。

所以,A 也可以完成屬性填充從而產(chǎn)生A 的初始化以后的實(shí)例并且將其放到一級(jí)緩存中。由于B之前使用的是A的實(shí)例是沒有做屬性填充的,也就是半成品的A實(shí)例,因此此時(shí)從一級(jí)緩存中獲取成品的A實(shí)例完成B對(duì)象的初始化。


? ? ? ? 圖2 A B 相互循環(huán)依賴,如何處理。

5、總結(jié)

本節(jié)課提出了Spring IoC遇到的循環(huán)依賴的問(wèn)題,并且通過(guò)分析bean創(chuàng)建的過(guò)程和三級(jí)緩存技術(shù),找到了解決singleton bean 循環(huán)依賴的辦法,然后通過(guò)一個(gè)簡(jiǎn)單的循環(huán)依賴處理的例子加強(qiáng)對(duì)這一思路的理解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容