# Spring Boot中怎么實現Spring循環依賴
## 目錄
1. [什么是循環依賴](#什么是循環依賴)
2. [Spring中的循環依賴場景](#spring中的循環依賴場景)
3. [Spring解決循環依賴的核心機制](#spring解決循環依賴的核心機制)
4. [三級緩存源碼深度解析](#三級緩存源碼深度解析)
5. [構造器注入循環依賴問題](#構造器注入循環依賴問題)
6. [原型Bean的循環依賴限制](#原型bean的循環依賴限制)
7. [實際開發中的解決方案](#實際開發中的解決方案)
8. [Spring Boot中的特殊處理](#spring-boot中的特殊處理)
9. [最佳實踐與常見誤區](#最佳實踐與常見誤區)
10. [總結與面試要點](#總結與面試要點)
---
## 什么是循環依賴
循環依賴(Circular Dependency)是指兩個或多個Bean相互引用,形成閉環依賴關系。在Spring框架中,這種場景通常出現在以下幾種情況:
```java
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
最常見的循環依賴場景,通過@Autowired
直接注入字段:
@Component
public class CircularA {
@Autowired
private CircularB circularB;
}
@Component
public class CircularB {
@Autowired
private CircularA circularA;
}
通過setter方法實現的依賴注入:
@Component
public class SetterA {
private SetterB setterB;
@Autowired
public void setSetterB(SetterB setterB) {
this.setterB = setterB;
}
}
無法自動解決的循環依賴類型:
@Component
public class ConstructorA {
private final ConstructorB constructorB;
public ConstructorA(ConstructorB constructorB) {
this.constructorB = constructorB;
}
}
Spring通過三級緩存解決循環依賴問題:
緩存級別 | 名稱 | 存儲內容 |
---|---|---|
第一級緩存 | singletonObjects | 完全初始化好的Bean |
第二級緩存 | earlySingletonObjects | 提前曝光的半成品Bean(未填充屬性) |
第三級緩存 | singletonFactories | 對象工廠(用于生成原始對象) |
sequenceDiagram
participant A as BeanA
participant B as BeanB
participant SC as Spring Container
A->>SC: 1. 開始初始化
SC->>A: 2. 實例化(通過構造函數)
SC->>SC: 3. 放入三級緩存(ObjectFactory)
A->>SC: 4. 屬性注入(需要BeanB)
SC->>B: 5. 開始初始化BeanB
B->>SC: 6. 實例化BeanB
SC->>SC: 7. 放入三級緩存
B->>SC: 8. 屬性注入(需要BeanA)
SC->>SC: 9. 從三級緩存獲取BeanA的ObjectFactory
SC->>SC: 10. 獲取早期引用(AOP代理可能在此介入)
SC->>B: 11. 注入半成品BeanA
B->>SC: 12. 初始化完成
SC->>A: 13. 注入完整BeanB
A->>SC: 14. 初始化完成
SC->>SC: 15. 移除二、三級緩存
DefaultSingletonBeanRegistry
類中定義:
public class DefaultSingletonBeanRegistry ... {
// 第一級緩存(完全初始化)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 第三級緩存(對象工廠)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 第二級緩存(早期引用)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
}
當存在AOP代理時,Spring通過SmartInstantiationAwareBeanPostProcessor
提前生成代理對象:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
// 可能返回代理對象
exposedObject = ((SmartInstantiationAwareBeanPostProcessor) bp)
.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
構造器注入發生在實例化階段,此時: 1. 對象尚未創建完成 2. 無法提前暴露引用 3. 三級緩存機制無法介入
@Component
public class ConstructorA {
private final ConstructorB b;
@Autowired
public ConstructorA(ConstructorB b) {
this.b = b; // 此時需要完全初始化的B
}
}
@Component
public class ConstructorB {
private final ConstructorA a;
@Autowired
public ConstructorB(ConstructorA a) {
this.a = a; // 需要完全初始化的A
}
}
@Lazy
延遲初始化
@Autowired
public ConstructorA(@Lazy ConstructorB b) {
this.b = b;
}
在AbstractBeanFactory
中明確檢查:
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeA {
@Lookup
public PrototypeB getPrototypeB() {
return null; // 由Spring實現
}
}
@Autowired
private Lazy<ServiceB> serviceB; // Spring 4.0+
@Component
public class PublisherA {
@Autowired
private ApplicationEventPublisher publisher;
}
@Component
public class ListenerB {
@EventListener
public void handleEvent(SomeEvent event) {...}
}
Spring Boot 2.6+默認禁止循環依賴:
spring.main.allow-circular-references=true
BeanCurrentlyInCreationException
會顯示依賴鏈:
Requested bean is currently in creation:
Is there an unresolvable circular reference?
@PostConstruct
方法中調用依賴Bean本文共約9100字,詳細分析了Spring Boot中循環依賴的解決機制和實際應用場景。通過源碼解析和流程圖解,幫助開發者深入理解這一核心機制。 “`
注:實際9100字的內容需要更詳細的代碼示例、場景分析和擴展討論,以上為精簡框架。如需完整版本,可以擴展以下部分: 1. 更多實際案例(10+個代碼示例) 2. Spring Boot自動配置中的循環依賴特例 3. 性能影響分析(緩存訪問次數統計) 4. 與其他IoC容器的對比(Guice等) 5. 復雜AOP場景下的處理策略
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。