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

循環(huán)依賴可能出現(xiàn)的場景
- 模塊之間的相互引用:在模塊化的軟件設(shè)計(jì)中,不同模塊之間可能會相互引用,特別是當(dāng)模塊之間存在交叉的功能需求或依賴時。如果模塊之間的依賴關(guān)系沒有正確管理,就可能產(chǎn)生循環(huán)依賴。
- 類的相互引用:在面向?qū)ο缶幊讨?,不同類之間可能會有相互引用,尤其是在它們之間存在雙向的依賴關(guān)系時。如果類的構(gòu)造函數(shù)或方法參數(shù)中出現(xiàn)了相互引用,就可能導(dǎo)致循環(huán)依賴問題。
- 依賴注入框架配置不當(dāng):依賴注入框架(如Spring)用于管理組件之間的依賴關(guān)系。如果配置文件中出現(xiàn)了循環(huán)依賴,框架可能無法正確初始化對象,導(dǎo)致異常。
- 事件和消息驅(qū)動的系統(tǒng):在事件和消息驅(qū)動的系統(tǒng)中,不同組件可能通過事件或消息進(jìn)行通信。如果事件的發(fā)起者和接收者之間出現(xiàn)相互依賴,就可能導(dǎo)致循環(huán)依賴。
- 單例模式的使用:當(dāng)使用單例模式時,如果多個單例對象之間相互依賴,可能會形成循環(huán)依賴。單例對象在整個應(yīng)用程序中只有一個實(shí)例,因此其依賴關(guān)系需要特別注意。
- 構(gòu)建和初始化順序問題:在某些情況下,對象的構(gòu)建和初始化順序可能導(dǎo)致循環(huán)依賴。如果某個對象在構(gòu)建階段需要引用另一個對象,而后者又需要在構(gòu)建階段引用前者,就可能形成循環(huán)依賴。
如何解決循環(huán)依賴
- 重構(gòu)設(shè)計(jì):重新審視組件之間的關(guān)系,嘗試將循環(huán)依賴問題轉(zhuǎn)化為單向的依賴關(guān)系。這可能需要重新劃分模塊或類的職責(zé),以減少相互依賴。
- 引入接口或抽象類:通過引入接口或抽象類,可以將具體的依賴關(guān)系替換為更高層次的抽象依賴,從而解耦循環(huán)依賴。
- 延遲初始化:將對象的初始化推遲到實(shí)際使用它們的時候,以避免在初始化階段出現(xiàn)循環(huán)依賴。這可以通過懶加載等方式來實(shí)現(xiàn)。
- 使用中介者模式:引入中介者或事件系統(tǒng),將類之間的通信轉(zhuǎn)移到中介者中,從而降低直接的循環(huán)依賴。這可以將相互依賴的關(guān)系集中在一個地方進(jìn)行處理。
- 使用依賴注入容器:依賴注入框架(如Spring)可以處理循環(huán)依賴問題。這些框架使用一些特殊的策略,以確保對象的正確初始化順序,從而解決循環(huán)依賴。
- 通過Setter注入或后處理器解決:在某些情況下,通過使用Setter方法注入依賴或使用依賴后處理器可以解決循環(huán)依賴問題。這樣,對象的構(gòu)建和初始化可以分為多個步驟。
- 更改對象創(chuàng)建時機(jī):有時,將對象的創(chuàng)建從構(gòu)造函數(shù)移至其他方法中,可以避免在構(gòu)造函數(shù)階段引發(fā)循環(huán)依賴。
- 設(shè)計(jì)模式:一些設(shè)計(jì)模式,如工廠方法模式、抽象工廠模式,可以用來分離對象的構(gòu)建和初始化,從而避免循環(huán)依賴。
到此這篇關(guān)于Spring循環(huán)依賴的處理的文章就介紹到這了,更多相關(guān)Spring循環(huán)依賴內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 線程的生命周期詳細(xì)介紹及實(shí)例代碼
這篇文章主要介紹了Java 線程的生命周期的相關(guān)資料,并附簡單實(shí)例代碼,幫助大家理解,需要的朋友可以參考下2016-10-10
Java連接數(shù)據(jù)庫實(shí)現(xiàn)方式
文章講述了Java連接MySQL數(shù)據(jù)庫的詳細(xì)步驟,包括下載和導(dǎo)入JDBC驅(qū)動、創(chuàng)建數(shù)據(jù)庫和表、以及編寫連接和讀取數(shù)據(jù)的代碼2024-11-11
springboot+WebMagic+MyBatis爬蟲框架的使用
本文是對spring boot+WebMagic+MyBatis做了整合,使用WebMagic爬取數(shù)據(jù),然后通過MyBatis持久化爬取的數(shù)據(jù)到mysql數(shù)據(jù)庫。具有一定的參考價值,感興趣的可以了解一下2021-08-08
集合嵌套之ArrayList嵌套ArrayList實(shí)例
下面小編就為大家?guī)硪黄锨短字瓵rrayList嵌套ArrayList實(shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
springcloud結(jié)合bytetcc實(shí)現(xiàn)數(shù)據(jù)強(qiáng)一致性原理解析
這篇文章主要介紹了springcloud結(jié)合bytetcc實(shí)現(xiàn)數(shù)據(jù)強(qiáng)一致性原理解析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03

