Spring循環(huán)依賴??的解決方式詳解
什么是循環(huán)依賴
循環(huán)依賴是指兩個(gè)或多個(gè)Bean相互依賴,形成一個(gè)閉環(huán)。例如:
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}在這個(gè)例子中,A依賴B,B又依賴A,形成了一個(gè)循環(huán)依賴鏈。
Spring解決循環(huán)依賴的機(jī)制
Spring通過三級(jí)緩存機(jī)制來解決循環(huán)依賴問題,具體實(shí)現(xiàn)如下:
三級(jí)緩存結(jié)構(gòu)
- 一級(jí)緩存(singletonObjects):存儲(chǔ)完全初始化好的Bean
- 二級(jí)緩存(earlySingletonObjects):存儲(chǔ)提前暴露的原始Bean(尚未填充屬性)
- 三級(jí)緩存(singletonFactories):存儲(chǔ)Bean工廠,用于生成原始Bean的早期引用

解決流程
Spring解決循環(huán)依賴的核心流程如下:

詳細(xì)步驟解析
創(chuàng)建Bean A:
- Spring容器開始創(chuàng)建Bean A
- 實(shí)例化A(調(diào)用構(gòu)造方法)
- 將A的ObjectFactory放入三級(jí)緩存(singletonFactories)
- 準(zhǔn)備填充A的屬性
發(fā)現(xiàn)依賴B:
- 在填充A的屬性時(shí),發(fā)現(xiàn)需要注入Bean B
- 容器開始創(chuàng)建Bean B
創(chuàng)建Bean B:
- 實(shí)例化B(調(diào)用構(gòu)造方法)
- 將B的ObjectFactory放入三級(jí)緩存(singletonFactories)
- 準(zhǔn)備填充B的屬性
發(fā)現(xiàn)依賴A:
- 在填充B的屬性時(shí),發(fā)現(xiàn)需要注入Bean A
- 容器嘗試從一級(jí)緩存獲取A(未找到)
- 從二級(jí)緩存獲取A(未找到)
- 從三級(jí)緩存獲取A的ObjectFactory并調(diào)用getObject()獲取早期引用
- 將A的早期引用放入二級(jí)緩存,并從三級(jí)緩存移除
完成B的創(chuàng)建:
- 將B的屬性填充完整
- 執(zhí)行B的初始化后處理器
- 將完整的B放入一級(jí)緩存
- 從二級(jí)和三級(jí)緩存中移除B的相關(guān)條目
完成A的創(chuàng)建:
- 現(xiàn)在A可以獲取到完整的B實(shí)例
- 將A的屬性填充完整
- 執(zhí)行A的初始化后處理器
- 將完整的A放入一級(jí)緩存
- 從二級(jí)緩存中移除A
代碼層面的實(shí)現(xiàn)
Spring解決循環(huán)依賴的核心代碼在DefaultSingletonBeanRegistry類中:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 首先檢查一級(jí)緩存
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 檢查二級(jí)緩存
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 檢查三級(jí)緩存
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 從三級(jí)緩存移到二級(jí)緩存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}循環(huán)依賴的限制
Spring并非能解決所有類型的循環(huán)依賴,有以下限制:
構(gòu)造器注入:無法解決構(gòu)造器注入的循環(huán)依賴
@Component
public class A {
private B b;
@Autowired
public A(B b) { this.b = b; }
}
@Component
public class B {
private A a;
@Autowired
public B(A a) { this.a = a; }
}這種情況會(huì)拋出BeanCurrentlyInCreationException
原型(prototype)作用域的Bean:Spring不支持原型Bean的循環(huán)依賴
@Async方法:如果循環(huán)依賴中包含@Async方法,也可能出現(xiàn)問題
最佳實(shí)踐
- 盡量避免循環(huán)依賴,設(shè)計(jì)時(shí)應(yīng)考慮解耦
- 如果必須使用循環(huán)依賴,優(yōu)先使用setter注入而非構(gòu)造器注入
- 考慮使用@Lazy注解延遲加載其中一個(gè)Bean
@Component
public class A {
@Lazy
@Autowired
private B b;
}總結(jié)
Spring通過三級(jí)緩存機(jī)制巧妙地解決了setter/field注入方式的循環(huán)依賴問題。理解這一機(jī)制不僅有助于我們避免開發(fā)中的循環(huán)依賴陷阱,也能更深入地理解Spring容器的Bean生命周期管理。在實(shí)際開發(fā)中,我們應(yīng)當(dāng)合理設(shè)計(jì)Bean之間的依賴關(guān)系,盡量避免循環(huán)依賴,當(dāng)確實(shí)需要時(shí),也要了解其背后的原理和限制。
以上就是Spring循環(huán)依賴??的解決方式詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring循環(huán)依賴的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java多線程編程之訪問共享對(duì)象和數(shù)據(jù)的方法
這篇文章主要介紹了Java多線程編程之訪問共享對(duì)象和數(shù)據(jù)的方法,多個(gè)線程訪問共享對(duì)象和數(shù)據(jù)的方式有兩種情況,本文分別給出代碼實(shí)例,需要的朋友可以參考下2015-05-05
Java JDK動(dòng)態(tài)代理實(shí)現(xiàn)原理實(shí)例解析
這篇文章主要介紹了Java JDK動(dòng)態(tài)代理實(shí)現(xiàn)原理實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
SpringBoot validator參數(shù)驗(yàn)證restful自定義錯(cuò)誤碼響應(yīng)方式
這篇文章主要介紹了SpringBoot validator參數(shù)驗(yàn)證restful自定義錯(cuò)誤碼響應(yīng)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
IDEA 設(shè)置顯示內(nèi)存的使用情況和內(nèi)存回收的方法
這篇文章主要介紹了IDEA 設(shè)置顯示內(nèi)存的使用情況和內(nèi)存回收的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
Spring Boot 中的任務(wù)執(zhí)行器基本概念及使用方法
務(wù)執(zhí)行器是 Spring Boot 中的一個(gè)非常實(shí)用的模塊,它可以簡(jiǎn)化異步任務(wù)的開發(fā)和管理,在本文中,我們介紹了任務(wù)執(zhí)行器的基本概念和使用方法,以及一個(gè)完整的示例代碼,需要的朋友可以參考下2023-07-07
springboot2學(xué)習(xí)世界著名程序springboot開發(fā)體驗(yàn)
這篇文章主要為大家介紹了世界著名程序springboot開發(fā)體驗(yàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
剖析Java中在Collection集合中使用contains和remove為什么要重寫equals
這篇文章主要介紹了Collection集合的contains和remove方法詳解remove以及相關(guān)的經(jīng)驗(yàn)技巧,通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09
如何使用Spring boot的@Transactional進(jìn)行事務(wù)管理
這篇文章介紹了SpringBoot中使用@Transactional注解進(jìn)行聲明式事務(wù)管理的詳細(xì)信息,包括基本用法、核心配置參數(shù)、關(guān)鍵注意事項(xiàng)、調(diào)試技巧、最佳實(shí)踐以及完整示例,感興趣的朋友一起看看吧2025-02-02

