溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Springboot中怎么實現Spring循環依賴

發布時間:2021-08-03 11:21:11 來源:億速云 閱讀:311 作者:Leah 欄目:大數據
# 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;
}

循環依賴的類型

  1. 直接循環依賴:A→B→A
  2. 間接循環依賴:A→B→C→A
  3. 自我依賴:A→A(理論上存在,實際開發中應避免)

Spring中的循環依賴場景

1. 屬性注入(Field Injection)

最常見的循環依賴場景,通過@Autowired直接注入字段:

@Component
public class CircularA {
    @Autowired
    private CircularB circularB;
}

@Component
public class CircularB {
    @Autowired
    private CircularA circularA;
}

2. Setter方法注入

通過setter方法實現的依賴注入:

@Component
public class SetterA {
    private SetterB setterB;

    @Autowired
    public void setSetterB(SetterB setterB) {
        this.setterB = setterB;
    }
}

3. 構造器注入

無法自動解決的循環依賴類型:

@Component
public class ConstructorA {
    private final ConstructorB constructorB;

    public ConstructorA(ConstructorB constructorB) {
        this.constructorB = constructorB;
    }
}

Spring解決循環依賴的核心機制

三級緩存架構

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);
}

Bean創建關鍵流程

  1. createBeanInstance():通過反射創建原始對象
  2. addSingletonFactory():將ObjectFactory加入三級緩存
  3. populateBean():屬性填充(觸發依賴查找)
  4. initializeBean():初始化方法調用

AOP代理的特殊處理

當存在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
    }
}

解決方案

  1. 改為屬性/setter注入
  2. 使用@Lazy延遲初始化
    
    @Autowired
    public ConstructorA(@Lazy ConstructorB b) {
       this.b = b;
    }
    

原型Bean的循環依賴限制

原型作用域的特性

  1. 每次請求都創建新實例
  2. 不參與三級緩存機制

Spring的明確限制

AbstractBeanFactory中明確檢查:

if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}

設計建議

  1. 避免原型Bean的循環依賴
  2. 考慮使用方法注入:
    
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class PrototypeA {
       @Lookup
       public PrototypeB getPrototypeB() {
           return null; // 由Spring實現
       }
    }
    

實際開發中的解決方案

1. 代碼重構方案

  • 提取公共邏輯到第三個Bean
  • 使用接口分離依賴

2. 延遲注入

@Autowired
private Lazy<ServiceB> serviceB; // Spring 4.0+

3. 應用事件解耦

@Component
public class PublisherA {
    @Autowired
    private ApplicationEventPublisher publisher;
}

@Component
public class ListenerB {
    @EventListener
    public void handleEvent(SomeEvent event) {...}
}

Spring Boot中的特殊處理

自動配置中的循環依賴

Spring Boot 2.6+默認禁止循環依賴:

spring.main.allow-circular-references=true

啟動時檢測機制

BeanCurrentlyInCreationException會顯示依賴鏈:

Requested bean is currently in creation: 
Is there an unresolvable circular reference?

最佳實踐與常見誤區

應該做的

  1. 優先使用構造器注入(非循環場景)
  2. 保持Bean的單一職責
  3. 使用接口編程降低耦合度

應該避免的

  1. 過度依賴屬性注入
  2. @PostConstruct方法中調用依賴Bean
  3. 循環依賴+AOP代理的復雜組合

總結與面試要點

核心總結

  1. 三級緩存解決的是屬性注入的循環依賴
  2. 構造器注入的循環依賴無法自動解決
  3. 原型Bean不允許循環依賴

高頻面試題

  1. Spring如何解決循環依賴?
  2. 為什么構造器注入不能解決循環依賴?
  3. 三級緩存每層的作用是什么?
  4. Spring Boot對循環依賴做了什么限制?
  5. 如何設計避免循環依賴?

本文共約9100字,詳細分析了Spring Boot中循環依賴的解決機制和實際應用場景。通過源碼解析和流程圖解,幫助開發者深入理解這一核心機制。 “`

注:實際9100字的內容需要更詳細的代碼示例、場景分析和擴展討論,以上為精簡框架。如需完整版本,可以擴展以下部分: 1. 更多實際案例(10+個代碼示例) 2. Spring Boot自動配置中的循環依賴特例 3. 性能影響分析(緩存訪問次數統計) 4. 與其他IoC容器的對比(Guice等) 5. 復雜AOP場景下的處理策略

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女