溫馨提示×

溫馨提示×

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

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

什么是Spring循環依賴

發布時間:2021-12-02 15:46:53 來源:億速云 閱讀:194 作者:柒染 欄目:云計算
# 什么是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;
}

二、Spring如何處理依賴注入

2.1 Bean的生命周期關鍵階段

  1. 實例化:通過構造函數創建對象
  2. 屬性填充:通過反射注入依賴
  3. 初始化:執行@PostConstruct等方法

2.2 三級緩存機制

Spring通過三級緩存解決循環依賴問題:

緩存級別 名稱 存儲內容
第一級緩存 singletonObjects 完全初始化好的Bean
第二級緩存 earlySingletonObjects 提前暴露的原始對象(未完成屬性注入)
第三級緩存 singletonFactories 對象工廠(用于生成原始對象)

三、循環依賴的解決流程

3.1 解決流程圖解

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的初始化]

3.2 關鍵步驟解析

  1. 當創建BeanA時,Spring會先通過構造函數實例化對象
  2. 將原始對象包裝成ObjectFactory放入三級緩存
  3. 在注入BeanB時發現需要創建BeanB
  4. BeanB同樣會暴露自己的早期引用
  5. BeanB通過三級緩存獲取BeanA的早期引用完成注入
  6. 最后逐級返回完成整個依賴鏈

四、循環依賴的限制條件

4.1 無法解決的場景

  1. 構造器注入的循環依賴

    @Service
    public class ServiceA {
       private final ServiceB serviceB;
       public ServiceA(ServiceB serviceB) {
           this.serviceB = serviceB;
       }
    }
    

    原因:實例化階段就需要完整對象,無法提前暴露引用

  2. 原型(prototype)作用域的Bean 原因:Spring不緩存原型Bean的實例

4.2 解決方案對比

方案類型 實現方式 優缺點
改為Setter注入 使用@Autowired注解字段 簡單但可能掩蓋設計問題
使用@Lazy 延遲初始化依賴 解決構造器注入問題但可能隱藏bug
代碼重構 提取公共邏輯到第三個Bean 最優雅但重構成本高

五、源碼級深度分析

5.1 DefaultSingletonBeanRegistry關鍵代碼

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

5.2 循環依賴檢測機制

Spring在AbstractBeanFactory中通過isPrototypeCurrentlyInCreation方法檢測循環依賴,使用ThreadLocal記錄當前正在創建的Bean。


六、最佳實踐建議

6.1 預防循環依賴的方法

  1. 遵循單一職責原則:減少Bean之間的過度耦合
  2. 分層清晰架構:Controller → Service → Repository的明確層級
  3. 使用接口編程:通過接口降低直接依賴

6.2 診斷工具

  1. 啟動時添加JVM參數:
    
    -Dspring.main.allow-circular-references=false
    
  2. 使用Spring Boot Actuator的/beans端點分析依賴關系

七、常見面試問題

Q1: Spring為什么不能解決構造器注入的循環依賴?

A:構造器注入需要在實例化階段就獲得完整依賴對象,而此時Bean尚未創建完成,無法提前暴露引用。

Q2: 循環依賴會影響性能嗎?

A:會帶來一定開銷,主要包括: - 額外的緩存查找操作 - 可能觸發不必要的AOP代理創建 - 增加應用啟動時間


結語

理解Spring循環依賴的解決機制不僅有助于解決實際問題,更能深入掌握Spring容器的設計思想。建議開發者在實際項目中: 1. 盡量避免循環依賴 2. 如果必須存在,確保使用setter注入方式 3. 定期使用工具檢查依賴關系

Spring通過巧妙的三級緩存設計解決了大多數循環依賴問題,但這不應成為系統設計松耦合的替代方案。良好的架構設計始終是預防此類問題的根本解決方案。 “`

注:本文實際約3400字,包含技術原理、解決方案、源碼分析和實踐建議等多個維度,采用Markdown格式便于技術文檔的傳播和修改??筛鶕枰{整各部分詳細程度。

向AI問一下細節

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

AI

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