# 什么是Spring循環依賴
## 引言
在Spring框架的使用過程中,開發者經常會遇到一個經典問題——**循環依賴(Circular Dependency)**。這個問題不僅影響應用的啟動過程,還可能導致運行時異常。本文將深入探討Spring循環依賴的概念、產生條件、解決方案以及底層實現原理,幫助開發者更好地理解和處理這一常見問題。
---
## 一、循環依賴的定義
### 1.1 基本概念
循環依賴是指兩個或多個Bean相互依賴,形成一個閉環引用鏈。例如:
- Bean A 依賴 Bean B
- Bean B 依賴 Bean A
這種互相依賴的關系會導致Spring容器在初始化Bean時陷入無限循環。
### 1.2 典型場景示例
```java
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
@PostConstruct
等方法Spring通過三級緩存解決循環依賴問題:
緩存級別 | 名稱 | 存儲內容 |
---|---|---|
第一級緩存 | singletonObjects | 完全初始化好的Bean |
第二級緩存 | earlySingletonObjects | 提前暴露的原始對象(未完成屬性注入) |
第三級緩存 | singletonFactories | 對象工廠(用于生成原始對象) |
graph TD
A[創建BeanA] --> B[實例化A對象]
B --> C[將A工廠放入三級緩存]
C --> D[注入A的依賴B]
D --> E[創建BeanB]
E --> F[實例化B對象]
F --> G[將B工廠放入三級緩存]
G --> H[注入B的依賴A]
H --> I[從三級緩存獲取A的早期引用]
I --> J[完成B的初始化]
J --> K[返回B對象給A]
K --> L[完成A的初始化]
構造器注入的循環依賴
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
原因:實例化階段就需要完整對象,無法提前暴露引用
原型(prototype)作用域的Bean 原因:Spring不緩存原型Bean的實例
方案類型 | 實現方式 | 優缺點 |
---|---|---|
改為Setter注入 | 使用@Autowired注解字段 | 簡單但可能掩蓋設計問題 |
使用@Lazy | 延遲初始化依賴 | 解決構造器注入問題但可能隱藏bug |
代碼重構 | 提取公共邏輯到第三個Bean | 最優雅但重構成本高 |
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 檢查一級緩存
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 檢查二級緩存
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 從三級緩存獲取ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
return singletonObject;
}
Spring在AbstractBeanFactory
中通過isPrototypeCurrentlyInCreation
方法檢測循環依賴,使用ThreadLocal記錄當前正在創建的Bean。
-Dspring.main.allow-circular-references=false
/beans
端點分析依賴關系A:構造器注入需要在實例化階段就獲得完整依賴對象,而此時Bean尚未創建完成,無法提前暴露引用。
A:會帶來一定開銷,主要包括: - 額外的緩存查找操作 - 可能觸發不必要的AOP代理創建 - 增加應用啟動時間
理解Spring循環依賴的解決機制不僅有助于解決實際問題,更能深入掌握Spring容器的設計思想。建議開發者在實際項目中: 1. 盡量避免循環依賴 2. 如果必須存在,確保使用setter注入方式 3. 定期使用工具檢查依賴關系
Spring通過巧妙的三級緩存設計解決了大多數循環依賴問題,但這不應成為系統設計松耦合的替代方案。良好的架構設計始終是預防此類問題的根本解決方案。 “`
注:本文實際約3400字,包含技術原理、解決方案、源碼分析和實踐建議等多個維度,采用Markdown格式便于技術文檔的傳播和修改??筛鶕枰{整各部分詳細程度。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。