Spring循環(huán)依賴(lài)的處理方法
循環(huán)依賴(lài)是指兩個(gè)或多個(gè)組件之間相互依賴(lài),形成一個(gè)閉環(huán),從而導(dǎo)致這些組件無(wú)法正確地被初始化或加載。這種情況可能會(huì)在軟件開(kāi)發(fā)中引起問(wèn)題,因?yàn)檠h(huán)依賴(lài)會(huì)導(dǎo)致初始化順序混亂,組件之間的關(guān)系變得復(fù)雜,甚至可能引發(fā)死鎖或其他不穩(wěn)定行為。
在編程中,循環(huán)依賴(lài)通常出現(xiàn)在模塊、類(lèi)、或者組件之間的相互引用上,導(dǎo)致編譯、加載或初始化過(guò)程中的問(wèn)題。在依賴(lài)注入(DI)框架中,循環(huán)依賴(lài)可能會(huì)導(dǎo)致對(duì)象的創(chuàng)建和初始化失敗。
@Service
public class TestService1 {
@Autowired
private TestService2 testService2;
public void test1() {
}
}
@Service
public class TestService2 {
@Autowired
private TestService1 testService1;
public void test2() {
}
}A 依賴(lài)于 B,而 B 也依賴(lài)于 A,形成了循環(huán)依賴(lài)。如果沒(méi)有適當(dāng)?shù)奶幚?,初始化這兩個(gè)類(lèi)的實(shí)例可能會(huì)導(dǎo)致問(wèn)題。這段代碼之所以能正常運(yùn)行,是因?yàn)镾pring內(nèi)部機(jī)制,解決了循環(huán)依賴(lài)的問(wèn)題。
Spring內(nèi)部有三級(jí)緩存:
- singletonObjects 一級(jí)緩存,用于保存實(shí)例化、注入、初始化完成的bean實(shí)例
- earlySingletonObjects 二級(jí)緩存,用于保存實(shí)例化完成的bean實(shí)例
- singletonFactories 三級(jí)緩存,用于保存bean創(chuàng)建工廠,以便于后面擴(kuò)展有機(jī)會(huì)創(chuàng)建代理對(duì)象。
Spring解決循環(huán)依賴(lài)步驟:
- Bean 的注冊(cè):首先,Spring會(huì)解析并注冊(cè)所有的Bean定義,但不會(huì)立即創(chuàng)建Bean的實(shí)例。
- 提前創(chuàng)建 Bean 實(shí)例:對(duì)于每個(gè)要?jiǎng)?chuàng)建的單例(
@Service默認(rèn)是單例作用域),Spring會(huì)提前創(chuàng)建一個(gè)半初始化的實(shí)例,并將其放入singletonFactories緩存中。這是為了解決循環(huán)依賴(lài)問(wèn)題。 - 開(kāi)始創(chuàng)建 TestService1:當(dāng)開(kāi)始創(chuàng)建
TestService1時(shí),發(fā)現(xiàn)它依賴(lài)于TestService2。因?yàn)?TestService2的半初始化實(shí)例已經(jīng)在singletonFactories緩存中,所以會(huì)使用該實(shí)例創(chuàng)建TestService1,并在singletonFactories緩存中移除TestService2的工廠。 - 開(kāi)始創(chuàng)建 TestService2:當(dāng)創(chuàng)建
TestService2時(shí),發(fā)現(xiàn)它依賴(lài)于TestService1,但由于TestService1的半初始化實(shí)例已經(jīng)存在,所以會(huì)使用該實(shí)例創(chuàng)建TestService2,并在singletonFactories緩存中移除TestService1的工廠。 - 完全初始化:一旦創(chuàng)建過(guò)程完成,Spring會(huì)執(zhí)行
TestService1和TestService2的初始化操作,包括注入和其他初始化方法。這將使它們變成完全初始化的Bean
圖解:

循環(huán)依賴(lài)可能出現(xiàn)的場(chǎng)景
- 模塊之間的相互引用:在模塊化的軟件設(shè)計(jì)中,不同模塊之間可能會(huì)相互引用,特別是當(dāng)模塊之間存在交叉的功能需求或依賴(lài)時(shí)。如果模塊之間的依賴(lài)關(guān)系沒(méi)有正確管理,就可能產(chǎn)生循環(huán)依賴(lài)。
- 類(lèi)的相互引用:在面向?qū)ο缶幊讨?,不同?lèi)之間可能會(huì)有相互引用,尤其是在它們之間存在雙向的依賴(lài)關(guān)系時(shí)。如果類(lèi)的構(gòu)造函數(shù)或方法參數(shù)中出現(xiàn)了相互引用,就可能導(dǎo)致循環(huán)依賴(lài)問(wèn)題。
- 依賴(lài)注入框架配置不當(dāng):依賴(lài)注入框架(如Spring)用于管理組件之間的依賴(lài)關(guān)系。如果配置文件中出現(xiàn)了循環(huán)依賴(lài),框架可能無(wú)法正確初始化對(duì)象,導(dǎo)致異常。
- 事件和消息驅(qū)動(dòng)的系統(tǒng):在事件和消息驅(qū)動(dòng)的系統(tǒng)中,不同組件可能通過(guò)事件或消息進(jìn)行通信。如果事件的發(fā)起者和接收者之間出現(xiàn)相互依賴(lài),就可能導(dǎo)致循環(huán)依賴(lài)。
- 單例模式的使用:當(dāng)使用單例模式時(shí),如果多個(gè)單例對(duì)象之間相互依賴(lài),可能會(huì)形成循環(huán)依賴(lài)。單例對(duì)象在整個(gè)應(yīng)用程序中只有一個(gè)實(shí)例,因此其依賴(lài)關(guān)系需要特別注意。
- 構(gòu)建和初始化順序問(wèn)題:在某些情況下,對(duì)象的構(gòu)建和初始化順序可能導(dǎo)致循環(huán)依賴(lài)。如果某個(gè)對(duì)象在構(gòu)建階段需要引用另一個(gè)對(duì)象,而后者又需要在構(gòu)建階段引用前者,就可能形成循環(huán)依賴(lài)。
如何解決循環(huán)依賴(lài)
- 重構(gòu)設(shè)計(jì):重新審視組件之間的關(guān)系,嘗試將循環(huán)依賴(lài)問(wèn)題轉(zhuǎn)化為單向的依賴(lài)關(guān)系。這可能需要重新劃分模塊或類(lèi)的職責(zé),以減少相互依賴(lài)。
- 引入接口或抽象類(lèi):通過(guò)引入接口或抽象類(lèi),可以將具體的依賴(lài)關(guān)系替換為更高層次的抽象依賴(lài),從而解耦循環(huán)依賴(lài)。
- 延遲初始化:將對(duì)象的初始化推遲到實(shí)際使用它們的時(shí)候,以避免在初始化階段出現(xiàn)循環(huán)依賴(lài)。這可以通過(guò)懶加載等方式來(lái)實(shí)現(xiàn)。
- 使用中介者模式:引入中介者或事件系統(tǒng),將類(lèi)之間的通信轉(zhuǎn)移到中介者中,從而降低直接的循環(huán)依賴(lài)。這可以將相互依賴(lài)的關(guān)系集中在一個(gè)地方進(jìn)行處理。
- 使用依賴(lài)注入容器:依賴(lài)注入框架(如Spring)可以處理循環(huán)依賴(lài)問(wèn)題。這些框架使用一些特殊的策略,以確保對(duì)象的正確初始化順序,從而解決循環(huán)依賴(lài)。
- 通過(guò)Setter注入或后處理器解決:在某些情況下,通過(guò)使用Setter方法注入依賴(lài)或使用依賴(lài)后處理器可以解決循環(huán)依賴(lài)問(wèn)題。這樣,對(duì)象的構(gòu)建和初始化可以分為多個(gè)步驟。
- 更改對(duì)象創(chuàng)建時(shí)機(jī):有時(shí),將對(duì)象的創(chuàng)建從構(gòu)造函數(shù)移至其他方法中,可以避免在構(gòu)造函數(shù)階段引發(fā)循環(huán)依賴(lài)。
- 設(shè)計(jì)模式:一些設(shè)計(jì)模式,如工廠方法模式、抽象工廠模式,可以用來(lái)分離對(duì)象的構(gòu)建和初始化,從而避免循環(huán)依賴(lài)。
到此這篇關(guān)于Spring循環(huán)依賴(lài)的處理的文章就介紹到這了,更多相關(guān)Spring循環(huán)依賴(lài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中ArrayList和LinkedList區(qū)別
這篇文章主要介紹了Java中ArrayList和LinkedList區(qū)別,下面我們就重點(diǎn)聊一聊在日常開(kāi)發(fā)中經(jīng)常被使用到的兩個(gè)集合類(lèi)ArrayList和LinkedList的本質(zhì)區(qū)別吧,需要的朋友可以參考一下2022-01-01
Java實(shí)戰(zhàn)之用hutool-db實(shí)現(xiàn)多數(shù)據(jù)源配置
在微服務(wù)搭建中經(jīng)常會(huì)使用到多數(shù)據(jù)庫(kù)情形這個(gè)時(shí)候,下面這篇文章主要給大家介紹了關(guān)于Java實(shí)戰(zhàn)之用hutool-db實(shí)現(xiàn)多數(shù)據(jù)源配置的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
基于Idea+Jconsole實(shí)現(xiàn)線程監(jiān)控步驟
這篇文章主要介紹了基于Idea+Jconsole實(shí)現(xiàn)線程監(jiān)控功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
Java中for(;;)和while(true)的區(qū)別
這篇文章主要介紹了 Java中for(;;)和while(true)的區(qū)別,文章圍繞for(;;)和while(true)的相關(guān)自來(lái)哦展開(kāi)詳細(xì)內(nèi)容,需要的小伙伴可以參考一下,希望對(duì)大家有所幫助2021-11-11
Maven在Windows中的配置以及IDE中的項(xiàng)目創(chuàng)建(圖文教程)
這篇文章主要介紹了Maven在Windows中的配置以及IDE中的項(xiàng)目創(chuàng)建(圖文教程),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
mybatis實(shí)現(xiàn)查詢(xún)操作及獲得添加的ID
這篇文章主要介紹了mybatis實(shí)現(xiàn)查詢(xún)操作及獲得添加的ID,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-11-11

