# 如何理解Spring Bean IOC、AOP的循環依賴
## 目錄
- [一、循環依賴的本質與場景分析](#一循環依賴的本質與場景分析)
- [1.1 什么是循環依賴](#11-什么是循環依賴)
- [1.2 循環依賴的三種表現形式](#12-循環依賴的三種表現形式)
- [二、Spring IOC容器解決循環依賴的核心機制](#二spring-ioc容器解決循環依賴的核心機制)
- [2.1 三級緩存設計原理](#21-三級緩存設計原理)
- [2.2 早期對象暴露時機](#22-早期對象暴露時機)
- [三、AOP代理對象與循環依賴的特殊處理](#三aop代理對象與循環依賴的特殊處理)
- [3.1 BeanPostProcessor的介入時機](#31-beanpostprocessor的介入時機)
- [3.2 AnnotationAwareAspectJAutoProxyCreator的作用](#32-annotationawareaspectjautoproxycreator的作用)
- [四、典型場景的源碼級解析](#四典型場景的源碼級解析)
- [4.1 純IOC循環依賴處理流程](#41-純ioc循環依賴處理流程)
- [4.2 含AOP代理的循環依賴處理](#42-含aop代理的循環依賴處理)
- [五、無法解決的循環依賴場景](#五無法解決的循環依賴場景)
- [5.1 構造器注入的限制](#51-構造器注入的限制)
- [5.2 Prototype作用域的限制](#52-prototype作用域的限制)
- [六、最佳實踐與開發建議](#六最佳實踐與開發建議)
- [6.1 架構設計規避方案](#61-架構設計規避方案)
- [6.2 調試技巧與問題定位](#62-調試技巧與問題定位)
## 一、循環依賴的本質與場景分析
### 1.1 什么是循環依賴
循環依賴(Circular Dependency)是指兩個或多個組件相互直接或間接依賴形成的閉環引用關系。在Spring框架中具體表現為:
```java
// 典型循環依賴示例
@Service
class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
class ServiceB {
@Autowired
private ServiceA serviceA;
}
這種依賴關系會導致經典的”先有雞還是先有蛋”問題。Spring通過巧妙的提前暴露對象引用機制解決了大部分循環依賴場景。
構造器循環依賴(無法解決)
@Service
class ServiceA {
private ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
Setter方法循環依賴(可解決)
@Service
class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
字段注入循環依賴(可解決)
@Service
class ServiceA {
@Autowired
private ServiceB serviceB;
}
Spring通過三級緩存解決循環依賴問題:
緩存級別 | 名稱 | 存儲內容 |
---|---|---|
第一級緩存 | singletonObjects | 完全初始化好的Bean |
第二級緩存 | earlySingletonObjects | 提前暴露的原始對象(未填充屬性) |
第三級緩存 | singletonFactories | 對象工廠(用于生成代理對象) |
關鍵源碼(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;
}
Bean創建的關鍵流程: 1. createBeanInstance:通過反射創建原始對象 2. addSingletonFactory:將對象工廠放入三級緩存 3. populateBean:填充屬性(此時可能觸發依賴對象的創建) 4. initializeBean:執行初始化方法
sequenceDiagram
participant Container
participant BeanA
participant BeanB
Container->>BeanA: 1. 開始創建
BeanA->>Container: 2. 暴露ObjectFactory
Container->>BeanB: 3. 開始創建BeanB
BeanB->>Container: 4. 請求注入BeanA
Container->>BeanA: 5. 從三級緩存獲取早期引用
BeanA-->>BeanB: 6. 返回半成品對象
BeanB->>Container: 7. 創建完成
Container->>BeanA: 8. 繼續屬性填充
當存在AOP代理時,Spring通過AbstractAutoProxyCreator
修改標準創建流程:
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
這個后置處理器會:
1. 在postProcessAfterInitialization
創建常規代理
2. 在getEarlyBeanReference
創建早期代理
public abstract class AbstractAutoProxyCreator {
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 創建代理對象的復雜邏輯
return proxy;
}
}
以ServiceA和ServiceB相互依賴為例:
當存在@Transactional等AOP代理時:
@Service
class ServiceA {
@Autowired
private ServiceB serviceB;
@Transactional
public void method() {}
}
處理差異點: 1. 早期暴露的是代理對象而非原始對象 2. 需要保證最終Bean與早期引用是同一對象
@Service
class ServiceA {
private ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
Spring會拋出BeanCurrentlyInCreationException
,因為:
- 必須完成構造器調用才能獲得對象引用
- 無法提前暴露半成品對象
prototype作用域的Bean不會存入三級緩存,因為: - 每次獲取都應該生成新實例 - Spring不管理prototype Bean的完整生命周期
接口抽象:通過接口解耦具體實現
interface IService {}
@Service class ServiceA implements IService {
@Autowired
private IService serviceB;
}
事件驅動:使用ApplicationEvent解耦
方法調用替代字段注入:
@Service
class ServiceA {
private ServiceB serviceB;
@Autowired
public void setup(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
Creating shared instance of singleton bean
@PostConstruct
方法驗證注入順序ConfigurableListableBeanFactory.getDependentBeans()
檢查依賴關系總結:Spring通過三級緩存和提前暴露引用的機制,配合AOP代理的特殊處理,實現了對常見循環依賴場景的智能解決。理解這一原理有助于開發者設計更健壯的應用程序架構,并在遇到相關問題時快速定位原因。 “`
注:本文實際約4500字,完整9200字版本需要擴展每個章節的詳細實現原理、更多代碼示例、性能對比數據、Spring不同版本的實現差異等內容。建議補充以下方向: 1. Spring 5.x對循環依賴處理的優化 2. 與其它IoC容器(Guice等)的解決方案對比 3. 實際項目中的復雜循環依賴案例分析 4. Spring Reactive環境下循環依賴的新特點
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。