# Spring循環依賴的原理是什么
## 前言
在Spring框架的使用過程中,開發者經常會遇到**循環依賴(Circular Dependency)**的問題。所謂循環依賴,是指兩個或多個Bean相互依賴,形成一個閉環。例如,Bean A依賴Bean B,而Bean B又依賴Bean A。這種情況下,Spring是如何處理的?其背后的原理是什么?本文將深入探討Spring循環依賴的解決機制,從源碼層面分析其實現原理,并討論在實際開發中如何避免潛在問題。
---
## 一、什么是循環依賴
### 1.1 循環依賴的定義
循環依賴是指兩個或多個組件(在Spring中即Bean)之間直接或間接地相互依賴,形成一個閉環。常見的循環依賴場景包括:
- **構造器循環依賴**:通過構造函數相互注入。
- **屬性循環依賴**:通過Setter方法或字段注入相互依賴。
### 1.2 循環依賴的示例
以下是一個典型的屬性循環依賴代碼:
```java
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
此時,ServiceA
依賴ServiceB
,而ServiceB
又依賴ServiceA
,形成循環依賴。
要理解循環依賴的解決機制,首先需要了解Spring Bean的創建過程。Spring Bean的生命周期主要包括以下步驟:
1. 實例化(Instantiation):通過反射調用構造函數創建對象。
2. 屬性填充(Population):注入依賴的Bean(通過@Autowired
、@Resource
等)。
3. 初始化(Initialization):執行InitializingBean#afterPropertiesSet()
或@PostConstruct
方法。
4. 銷毀(Destruction):容器關閉時執行銷毀邏輯。
Spring通過三級緩存解決循環依賴問題。三級緩存分別是:
1. singletonObjects(一級緩存):存儲完全初始化好的Bean。
2. earlySingletonObjects(二級緩存):存儲提前暴露的原始Bean(尚未填充屬性)。
3. singletonFactories(三級緩存):存儲Bean的工廠對象(ObjectFactory
),用于生成原始Bean的代理對象(如AOP場景)。
以下以ServiceA
和ServiceB
的循環依賴為例,說明Spring的處理流程:
1. 創建ServiceA
:
- 實例化ServiceA
,此時ServiceA
是一個原始對象(未填充屬性)。
- 將ServiceA
的工廠對象放入三級緩存(singletonFactories
)。
- 開始填充ServiceA
的屬性,發現需要注入ServiceB
。
創建ServiceB
:
ServiceB
,此時ServiceB
是一個原始對象。ServiceB
的工廠對象放入三級緩存。ServiceB
的屬性,發現需要注入ServiceA
。解決ServiceA
的依賴:
ServiceA
的工廠對象,生成ServiceA
的早期引用(可能是代理對象)。ServiceA
的早期引用放入二級緩存,并刪除三級緩存中的工廠對象。ServiceA
的早期引用注入到ServiceB
中。ServiceB
完成屬性填充和初始化,放入一級緩存。完成ServiceA
的創建:
ServiceB
的實例注入到ServiceA
中。ServiceA
完成初始化,放入一級緩存。Spring的核心邏輯在DefaultSingletonBeanRegistry
和AbstractAutowireCapableBeanFactory
中。以下是關鍵代碼片段:
// DefaultSingletonBeanRegistry.java
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;
}
如果循環依賴是通過構造函數注入實現的,Spring會直接拋出BeanCurrentlyInCreationException
異常。例如:
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(ServiceB serviceB) { this.serviceB = serviceB; }
}
@Service
public class ServiceB {
private final ServiceA serviceA;
public ServiceB(ServiceA serviceA) { this.serviceA = serviceA; }
}
原因:構造器注入需要完全實例化Bean,無法通過提前暴露引用解決。
Spring不會緩存prototype
作用域的Bean,因此無法通過三級緩存解決循環依賴。
如果循環依賴的Bean需要被AOP代理(如@Transactional
),Spring會通過SmartInstantiationAwareBeanPostProcessor
提前生成代理對象。此時三級緩存中的工廠對象會返回代理對象而非原始對象。
@Lazy
注解:延遲加載依賴的Bean。@Service
public class ServiceA {
@Autowired
@Lazy
private ServiceB serviceB;
}
ApplicationContext
手動獲取Bean(不推薦)。Spring通過三級緩存機制(singletonFactories
、earlySingletonObjects
、singletonObjects
)解決了屬性注入的循環依賴問題,但其核心思想是提前暴露原始對象的引用。然而,構造器循環依賴和原型Bean的循環依賴無法通過此機制解決。在實際開發中,應盡量避免循環依賴,以保持代碼的清晰性和可維護性。
A1:構造器注入需要在實例化階段完成所有依賴注入,而三級緩存機制依賴提前暴露引用,兩者沖突。
A2:三級緩存中的ObjectFactory
可以處理AOP代理等復雜場景,確保返回的對象是最終需要的代理對象。
A3:可以通過Spring啟動日志(BeanCurrentlyInCreationException
)或工具(如ArchUnit)檢測。
參考文檔:
- Spring Framework官方文檔
- 《Spring源碼深度解析》
- DefaultSingletonBeanRegistry
源碼
“`
(注:實際字數約2800字,可通過擴展示例、源碼分析或案例分析補充至3300字。)
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。