溫馨提示×

溫馨提示×

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

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

Spring循環依賴的原理是什么

發布時間:2021-07-24 13:51:03 來源:億速云 閱讀:288 作者:Leah 欄目:編程語言
# 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如何處理循環依賴

2.1 Spring Bean的生命周期

要理解循環依賴的解決機制,首先需要了解Spring Bean的創建過程。Spring Bean的生命周期主要包括以下步驟: 1. 實例化(Instantiation):通過反射調用構造函數創建對象。 2. 屬性填充(Population):注入依賴的Bean(通過@Autowired、@Resource等)。 3. 初始化(Initialization):執行InitializingBean#afterPropertiesSet()@PostConstruct方法。 4. 銷毀(Destruction):容器關閉時執行銷毀邏輯。

2.2 三級緩存機制

Spring通過三級緩存解決循環依賴問題。三級緩存分別是: 1. singletonObjects(一級緩存):存儲完全初始化好的Bean。 2. earlySingletonObjects(二級緩存):存儲提前暴露的原始Bean(尚未填充屬性)。 3. singletonFactories(三級緩存):存儲Bean的工廠對象(ObjectFactory),用于生成原始Bean的代理對象(如AOP場景)。

2.3 解決循環依賴的流程

以下以ServiceAServiceB的循環依賴為例,說明Spring的處理流程: 1. 創建ServiceA: - 實例化ServiceA,此時ServiceA是一個原始對象(未填充屬性)。 - 將ServiceA的工廠對象放入三級緩存(singletonFactories)。 - 開始填充ServiceA的屬性,發現需要注入ServiceB。

  1. 創建ServiceB

    • 實例化ServiceB,此時ServiceB是一個原始對象。
    • ServiceB的工廠對象放入三級緩存。
    • 填充ServiceB的屬性,發現需要注入ServiceA。
  2. 解決ServiceA的依賴

    • 從三級緩存中獲取ServiceA的工廠對象,生成ServiceA的早期引用(可能是代理對象)。
    • ServiceA的早期引用放入二級緩存,并刪除三級緩存中的工廠對象。
    • ServiceA的早期引用注入到ServiceB中。
    • ServiceB完成屬性填充和初始化,放入一級緩存。
  3. 完成ServiceA的創建

    • ServiceB的實例注入到ServiceA中。
    • ServiceA完成初始化,放入一級緩存。

2.4 關鍵源碼分析

Spring的核心邏輯在DefaultSingletonBeanRegistryAbstractAutowireCapableBeanFactory中。以下是關鍵代碼片段:

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

三、循環依賴的局限性

3.1 構造器循環依賴無法解決

如果循環依賴是通過構造函數注入實現的,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,無法通過提前暴露引用解決。

3.2 原型作用域的Bean不支持循環依賴

Spring不會緩存prototype作用域的Bean,因此無法通過三級緩存解決循環依賴。

3.3 AOP代理的循環依賴

如果循環依賴的Bean需要被AOP代理(如@Transactional),Spring會通過SmartInstantiationAwareBeanPostProcessor提前生成代理對象。此時三級緩存中的工廠對象會返回代理對象而非原始對象。


四、如何避免循環依賴

4.1 設計層面

  • 遵循單一職責原則:減少Bean之間的耦合。
  • 使用接口編程:通過接口解耦具體實現。
  • 重新設計依賴關系:將雙向依賴改為單向依賴。

4.2 技術層面

  • 使用@Lazy注解:延遲加載依賴的Bean。
@Service
public class ServiceA {
    @Autowired
    @Lazy
    private ServiceB serviceB;
}
  • 改用Setter注入:避免構造器循環依賴。
  • 使用ApplicationContext手動獲取Bean(不推薦)。

五、總結

Spring通過三級緩存機制(singletonFactories、earlySingletonObjects、singletonObjects)解決了屬性注入的循環依賴問題,但其核心思想是提前暴露原始對象的引用。然而,構造器循環依賴和原型Bean的循環依賴無法通過此機制解決。在實際開發中,應盡量避免循環依賴,以保持代碼的清晰性和可維護性。


附錄:常見問題解答

Q1:為什么構造器循環依賴無法解決?

A1:構造器注入需要在實例化階段完成所有依賴注入,而三級緩存機制依賴提前暴露引用,兩者沖突。

Q2:Spring為什么選擇三級緩存而不是二級緩存?

A2:三級緩存中的ObjectFactory可以處理AOP代理等復雜場景,確保返回的對象是最終需要的代理對象。

Q3:如何檢測項目中的循環依賴?

A3:可以通過Spring啟動日志(BeanCurrentlyInCreationException)或工具(如ArchUnit)檢測。


參考文檔: - Spring Framework官方文檔 - 《Spring源碼深度解析》 - DefaultSingletonBeanRegistry源碼 “`

(注:實際字數約2800字,可通過擴展示例、源碼分析或案例分析補充至3300字。)

向AI問一下細節

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

AI

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