Spring?Boot解決循環(huán)依賴的過(guò)程詳細(xì)記錄
循環(huán)依賴
一、前言
環(huán)依賴是指兩個(gè)或者多個(gè)bean互相依賴對(duì)方,從而形成一個(gè)閉環(huán)。例如:Bean A依賴于Bean B,而B(niǎo)ean B又依賴于Bean A??赡軙?huì)導(dǎo)致Spring在嘗試創(chuàng)建這些bean實(shí)例時(shí)出現(xiàn)問(wèn)題,因?yàn)樗麄兓ハ嗟却龑?duì)方被創(chuàng)建,最終導(dǎo)致應(yīng)用程序無(wú)法啟動(dòng)。
Spring是如何發(fā)現(xiàn)這種循環(huán)依賴的問(wèn)題的呢?
通過(guò)依賴圖來(lái)檢測(cè)和發(fā)現(xiàn)循環(huán)依賴問(wèn)題。如下步驟:
二、Bean的創(chuàng)建過(guò)程
Spring容器在啟動(dòng)時(shí),會(huì)掃描配置文件(appliactionContext.xml)或者注解定義的bean,并且嘗試創(chuàng)建這些bean的實(shí)例。創(chuàng)建bean實(shí)例的過(guò)程如下
- 實(shí)例化:創(chuàng)建bean的實(shí)例。
- 屬性填充:為bean注入依賴其他的bean。
- 初始化:執(zhí)行自定義的初始化方法。
- 依賴注入過(guò)程
在屬性填充階段,Spring會(huì)為每個(gè)bean注入他所依賴的bean。在這個(gè)過(guò)程中,Spring會(huì)跟蹤哪些bean正在被創(chuàng)建,以便檢測(cè)循環(huán)依賴。
三、 循環(huán)依賴檢測(cè)機(jī)制
Spring通過(guò)一個(gè)名為“DefaultSingletonBeanRegistry”的類來(lái)跟蹤單例bean的創(chuàng)建狀態(tài)。該類維護(hù)了三個(gè)主要的緩存來(lái)管理bean的創(chuàng)建過(guò)程。
- singletonObjects:一級(jí)緩存(存儲(chǔ)完全初始化好的bean)
- earlySingletonObjects:二級(jí)緩存(存儲(chǔ)早期暴露的單例bean,用于解決循環(huán)依賴)
- singletonFactories:三級(jí)緩存(存儲(chǔ)用于創(chuàng)建bean實(shí)例的工廠)
具體的檢測(cè)步驟
3.1 實(shí)例化階段
當(dāng)Spring開(kāi)始實(shí)例化一個(gè)bean時(shí),它會(huì)將這個(gè)bean標(biāo)記為正在創(chuàng)建。這一步是通過(guò)將bean名稱添加到一個(gè)“正在創(chuàng)建中的bean”集合(‘singletonCurrentlyInCreaontion’)中來(lái)實(shí)現(xiàn)的。
3.2 屬性填充階段
在屬性填充階段,Sping會(huì)為該bean注入其依賴的其他的bean。此時(shí)Spring會(huì)檢查這個(gè)“其他的bean”是否已經(jīng)在創(chuàng)建過(guò)程中。
如果依賴的bean已經(jīng)在創(chuàng)建中,Spring會(huì)檢測(cè)到循環(huán)依賴,并根據(jù)不同的注入方式采取不同的處理方式。
如果是構(gòu)造函數(shù)注入,Spring會(huì)拋出‘BeanCurrentlyInCreationException’,因?yàn)闊o(wú)法解決構(gòu)造函數(shù)中注入的循環(huán)依賴。
如果是Setter注入,Spring會(huì)從‘earlySingletonObjects’或‘singletonFactories’中獲取依賴的bean,提前暴露一個(gè)部分創(chuàng)建的bean引用來(lái)解決循環(huán)依賴。
舉例:
如Bean A和Bean B循環(huán)依賴
@Component public class A { @Autowired private B b; public A() { System.out.println("A is created"); } } @Component public class B { @Autowired private A a; public B() { System.out.println("B is created"); } }
3.3 依賴注入
Spring的依賴注入過(guò)程:
- 實(shí)例化Bean A
將Bean A 標(biāo)記為正在創(chuàng)建,并添加到‘singletonCurrentlyInCreation’集合中。
實(shí)例化Bean A,并將其放入到三級(jí)緩存‘singletonFactories’中。(三級(jí)緩存存放的是創(chuàng)建實(shí)例化的bean工廠)
- 填充Bean A的屬性
此時(shí)發(fā)現(xiàn)Bean A依賴Bean B ,于是開(kāi)始創(chuàng)建Bean B。
將Bean B標(biāo)記為正在創(chuàng)建,并放入到‘singletonCurrentlyInCreation’集合中。
實(shí)例化Bean B,并將其放入到三級(jí)緩存‘singletonFactories’中。
- 填充Bean B的屬性
發(fā)現(xiàn)Bean B依賴于Bean A 。此時(shí),檢查到Bean A 已經(jīng)在創(chuàng)建過(guò)程中,因此發(fā)現(xiàn)了循環(huán)依賴。
由于是Setter方法注入,Spring會(huì)從三級(jí)緩存‘singletonFactories’中獲取一個(gè)部分創(chuàng)建的Bean A實(shí)例,提前暴露出來(lái),放入二級(jí)緩存‘earlySingletonObjects’中。
使用這個(gè)部分創(chuàng)建出來(lái)的Bean A實(shí)例 來(lái)填充Bean B的屬性。
- 完成Bean B的創(chuàng)建
Bean B中的所有屬性填充完畢后,Spring將Bean B的實(shí)例從三級(jí)緩存‘singletonFactories’中移到一級(jí)緩存‘singletonObjects’完全初始化好的bean中。
- 回到Bean A的創(chuàng)建
繼續(xù)為Bean A填充屬性,此時(shí)可以從一級(jí)緩存‘singletonObejcts’中獲取到完整的Bean B實(shí)例。
完成Bean A的創(chuàng)建,并將其從三級(jí)緩存‘singletonFactories’中移到一級(jí)緩存‘singletonObjects’中。
總結(jié)
到此這篇關(guān)于Spring Boot解決循環(huán)依賴的文章就介紹到這了,更多相關(guān)Spring Boot循環(huán)依賴解決內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
windows 32位eclipse遠(yuǎn)程hadoop開(kāi)發(fā)環(huán)境搭建
這篇文章主要介紹了windows 32位eclipse遠(yuǎn)程hadoop開(kāi)發(fā)環(huán)境搭建的相關(guān)資料,需要的朋友可以參考下2016-07-07springboot+vue+elementsUI實(shí)現(xiàn)分角色注冊(cè)登錄界面功能
這篇文章主要給大家介紹了關(guān)于springboot+vue+elementsUI實(shí)現(xiàn)分角色注冊(cè)登錄界面功能的相關(guān)資料,Spring?Boot和Vue.js是兩個(gè)非常流行的開(kāi)源框架,可以用來(lái)構(gòu)建Web應(yīng)用程序,需要的朋友可以參考下2023-07-07詳解Java中ByteArray字節(jié)數(shù)組的輸入輸出流的用法
ByteArrayInputStream和ByteArrayOutputStream分別集成自InputStream和OutputStream這兩個(gè)輸入和輸出流,這里我們就來(lái)詳解Java中ByteArray字節(jié)數(shù)組的輸入輸出流的用法,需要的朋友可以參考下2016-06-06Mybatis-Plus3.2.0 MetaObjectHandler 無(wú)法進(jìn)行公共字段全局填充
這篇文章主要介紹了Mybatis-Plus3.2.0 MetaObjectHandler 無(wú)法進(jìn)行公共字段全局填充,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11SpringBoot Maven Clean報(bào)錯(cuò)解決方案
這篇文章主要介紹了SpringBoot Maven Clean報(bào)錯(cuò)解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03java 實(shí)現(xiàn)約瑟夫環(huán)的實(shí)例代碼
這一次是借鑒模仿別人寫(xiě)的代碼,以前覺(jué)得不好將數(shù)據(jù)結(jié)構(gòu)的鏈結(jié)構(gòu)什么的遷移到j(luò)ava上來(lái)使用,但這一次確實(shí)讓我感受到了可以自己構(gòu)造數(shù)據(jù)結(jié)構(gòu),然后使用類似鏈的方式來(lái)解決約瑟夫環(huán),有所頓悟。不多說(shuō),繼續(xù)上代碼2013-10-10Gradle環(huán)境下導(dǎo)出Swagger為PDF的步驟詳解
這篇文章主要介紹了Gradle環(huán)境下導(dǎo)出Swagger為PDF的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06Spring boot使用spring retry重試機(jī)制的方法示例
這篇文章主要介紹了Spring boot使用spring retry重試機(jī)制的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01Java利用Redis實(shí)現(xiàn)高并發(fā)計(jì)數(shù)器的示例代碼
這篇文章主要介紹了Java利用Redis實(shí)現(xiàn)高并發(fā)計(jì)數(shù)器的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02