Spring循環(huán)依賴??的解決方式詳解
什么是循環(huán)依賴
循環(huán)依賴是指兩個或多個Bean相互依賴,形成一個閉環(huán)。例如:
@Component public class A { @Autowired private B b; } @Component public class B { @Autowired private A a; }
在這個例子中,A依賴B,B又依賴A,形成了一個循環(huán)依賴鏈。
Spring解決循環(huán)依賴的機(jī)制
Spring通過三級緩存機(jī)制來解決循環(huán)依賴問題,具體實(shí)現(xiàn)如下:
三級緩存結(jié)構(gòu)
- 一級緩存(singletonObjects):存儲完全初始化好的Bean
- 二級緩存(earlySingletonObjects):存儲提前暴露的原始Bean(尚未填充屬性)
- 三級緩存(singletonFactories):存儲Bean工廠,用于生成原始Bean的早期引用
解決流程
Spring解決循環(huán)依賴的核心流程如下:
詳細(xì)步驟解析
創(chuàng)建Bean A:
- Spring容器開始創(chuàng)建Bean A
- 實(shí)例化A(調(diào)用構(gòu)造方法)
- 將A的ObjectFactory放入三級緩存(singletonFactories)
- 準(zhǔn)備填充A的屬性
發(fā)現(xiàn)依賴B:
- 在填充A的屬性時,發(fā)現(xiàn)需要注入Bean B
- 容器開始創(chuàng)建Bean B
創(chuàng)建Bean B:
- 實(shí)例化B(調(diào)用構(gòu)造方法)
- 將B的ObjectFactory放入三級緩存(singletonFactories)
- 準(zhǔn)備填充B的屬性
發(fā)現(xiàn)依賴A:
- 在填充B的屬性時,發(fā)現(xiàn)需要注入Bean A
- 容器嘗試從一級緩存獲取A(未找到)
- 從二級緩存獲取A(未找到)
- 從三級緩存獲取A的ObjectFactory并調(diào)用getObject()獲取早期引用
- 將A的早期引用放入二級緩存,并從三級緩存移除
完成B的創(chuàng)建:
- 將B的屬性填充完整
- 執(zhí)行B的初始化后處理器
- 將完整的B放入一級緩存
- 從二級和三級緩存中移除B的相關(guān)條目
完成A的創(chuàng)建:
- 現(xiàn)在A可以獲取到完整的B實(shí)例
- 將A的屬性填充完整
- 執(zhí)行A的初始化后處理器
- 將完整的A放入一級緩存
- 從二級緩存中移除A
代碼層面的實(shí)現(xiàn)
Spring解決循環(huán)依賴的核心代碼在DefaultSingletonBeanRegistry
類中:
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 首先檢查一級緩存 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 檢查二級緩存 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 檢查三級緩存 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); // 從三級緩存移到二級緩存 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; } }
這種情況會拋出BeanCurrentlyInCreationException
原型(prototype)作用域的Bean:Spring不支持原型Bean的循環(huán)依賴
@Async方法:如果循環(huán)依賴中包含@Async方法,也可能出現(xiàn)問題
最佳實(shí)踐
- 盡量避免循環(huán)依賴,設(shè)計時應(yīng)考慮解耦
- 如果必須使用循環(huán)依賴,優(yōu)先使用setter注入而非構(gòu)造器注入
- 考慮使用@Lazy注解延遲加載其中一個Bean
@Component public class A { @Lazy @Autowired private B b; }
總結(jié)
Spring通過三級緩存機(jī)制巧妙地解決了setter/field注入方式的循環(huán)依賴問題。理解這一機(jī)制不僅有助于我們避免開發(fā)中的循環(huán)依賴陷阱,也能更深入地理解Spring容器的Bean生命周期管理。在實(shí)際開發(fā)中,我們應(yīng)當(dāng)合理設(shè)計Bean之間的依賴關(guān)系,盡量避免循環(huán)依賴,當(dāng)確實(shí)需要時,也要了解其背后的原理和限制。
以上就是Spring循環(huán)依賴??的解決方式詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring循環(huán)依賴的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JAVA中整型數(shù)組、字符串?dāng)?shù)組、整型數(shù)和字符串 的創(chuàng)建與轉(zhuǎn)換的方法
本文介紹了Java中字符串、字符數(shù)組和整型數(shù)組的創(chuàng)建方法,以及它們之間的轉(zhuǎn)換方法,還詳細(xì)講解了字符串中的一些常用方法,如indexOf()方法,并通過一個算法題目來應(yīng)用這些知識,感興趣的朋友一起看看吧2025-01-01java中struts2實(shí)現(xiàn)文件上傳下載功能實(shí)例解析
這篇文章主要介紹了java中struts2實(shí)現(xiàn)文件上傳下載功能的方法,以實(shí)例形式較為詳細(xì)的分析了struts2實(shí)現(xiàn)文件上傳下載功能的具體實(shí)現(xiàn)技巧與相關(guān)問題的解決方法,具有一定的參考借鑒價值,需要的朋友可以參考下2015-01-01Java 中HashCode作用_動力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了Java 中HashCode作用以及hashcode對于一個對象的重要性,對java中hashcode的作用相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧2017-05-05JavaWeb開發(fā)基于ssm的校園服務(wù)系統(tǒng)(實(shí)例詳解)
這篇文章主要介紹了JavaWeb開發(fā)基于ssm的校園服務(wù)系統(tǒng),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02netty服務(wù)端輔助類ServerBootstrap創(chuàng)建邏輯分析
這篇文章主要介紹了netty服務(wù)端輔助類ServerBootstrap創(chuàng)建邏輯分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03