溫馨提示×

溫馨提示×

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

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

Spring源碼分析之如何解決循環依賴

發布時間:2021-10-25 16:03:28 來源:億速云 閱讀:149 作者:iii 欄目:編程語言
# Spring源碼分析之如何解決循環依賴

## 目錄
1. [引言](#引言)
2. [什么是循環依賴](#什么是循環依賴)
3. [Spring中的循環依賴場景](#spring中的循環依賴場景)
4. [Spring解決循環依賴的核心設計](#spring解決循環依賴的核心設計)
5. [三級緩存機制詳解](#三級緩存機制詳解)
6. [源碼深度剖析](#源碼深度剖析)
7. [循環依賴的局限性](#循環依賴的局限性)
8. [實際開發中的建議](#實際開發中的建議)
9. [總結](#總結)

## 引言

在Spring框架的使用過程中,循環依賴(Circular Dependency)是一個常見但又容易引發問題的話題。Spring通過巧妙的設計解決了大部分循環依賴場景,但其內部實現機制卻鮮為人知。本文將深入Spring源碼(以5.3.x版本為例),剖析Spring如何通過三級緩存機制解決循環依賴問題。

## 什么是循環依賴

### 基本概念
循環依賴是指兩個或多個Bean相互依賴,形成閉環引用關系:
```java
// 示例1:直接循環依賴
class A {
    @Autowired B b;
}

class B {
    @Autowired A a;
}

// 示例2:間接循環依賴
class X {
    @Autowired Y y;
}

class Y {
    @Autowired Z z;
}

class Z {
    @Autowired X x;
}

循環依賴的類型

  1. 構造器循環依賴:通過構造函數注入形成的循環
  2. Setter循環依賴:通過setter方法或字段注入形成的循環
  3. 原型(prototype)作用域的循環依賴

Spring中的循環依賴場景

可解決的循環依賴

  • 單例Bean的setter/field注入循環依賴(最常見場景)

不可解決的循環依賴

  1. 構造器注入的循環依賴 “`java @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; } }

   啟動時會拋出`BeanCurrentlyInCreationException`

2. **prototype作用域的循環依賴**
   ```java
   @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
   @Service
   public class PrototypeA {
       @Autowired private PrototypeB b;
   }

Spring解決循環依賴的核心設計

三級緩存架構

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

緩存級別 名稱 存儲內容
第一級緩存 singletonObjects 完全初始化好的Bean
第二級緩存 earlySingletonObjects 提前暴露的原始Bean(未填充屬性)
第三級緩存 singletonFactories 單例工廠ObjectFactory

關鍵接口與類

  1. DefaultSingletonBeanRegistry:緩存管理的核心類
  2. AbstractAutowireCapableBeanFactory:Bean創建的主要邏輯
  3. SmartInstantiationAwareBeanPostProcessor:AOP代理相關處理

三級緩存機制詳解

緩存工作流程圖

graph TD
    A[開始創建BeanA] --> B[實例化A]
    B --> C[將A的ObjectFactory放入三級緩存]
    C --> D[填充A的屬性]
    D --> E[發現依賴BeanB]
    E --> F[開始創建BeanB]
    F --> G[實例化B]
    G --> H[將B的ObjectFactory放入三級緩存]
    H --> I[填充B的屬性]
    I --> J[發現依賴BeanA]
    J --> K[從三級緩存獲取A的ObjectFactory]
    K --> L[執行getEarlyBeanReference]
    L --> M[將A的半成品放入二級緩存]
    M --> N[返回A的早期引用給B]
    N --> O[完成B的初始化]
    O --> P[將B放入一級緩存]
    P --> Q[返回B的引用給A]
    Q --> R[完成A的初始化]
    R --> S[將A放入一級緩存]

關鍵源碼片段分析

1. 緩存獲取邏輯

DefaultSingletonBeanRegistry#getSingleton:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1. 檢查一級緩存
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 2. 檢查二級緩存
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 3. 檢查三級緩存
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

2. 添加SingletonFactory

AbstractAutowireCapableBeanFactory#doCreateBean:

protected Object doCreateBean(/*...*/) {
    // 實例化Bean
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    
    // 將ObjectFactory加入三級緩存
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    
    // 填充屬性
    populateBean(beanName, mbd, instanceWrapper);
    
    // 初始化
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    return exposedObject;
}

源碼深度剖析

Bean創建全流程

1. 入口:AbstractApplicationContext#refresh

public void refresh() {
    // ...
    finishBeanFactoryInitialization(beanFactory);
    // ...
}

2. 預實例化單例:DefaultListableBeanFactory#preInstantiateSingletons

public void preInstantiateSingletons() {
    for (String beanName : beanNames) {
        getBean(beanName);
    }
}

3. 核心創建邏輯:AbstractBeanFactory#doGetBean

protected <T> T doGetBean(/*...*/) {
    // 檢查緩存
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null) {
        return (T) sharedInstance;
    }
    
    // 創建Bean
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            return createBean(beanName, mbd, args);
        });
    }
}

AOP代理與循環依賴

當存在AOP代理時,getEarlyBeanReference會通過AbstractAutoProxyCreator提前生成代理對象:

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. 構造器注入為何不支持

在構造器注入場景下,Bean尚未創建完成就無法放入緩存,導致無法解決循環依賴:

protected BeanWrapper createBeanInstance(/*...*/) {
    // 構造器注入會直接嘗試獲取依賴Bean
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null) {
        return autowireConstructor(beanName, mbd, ctors, args); // 這里會立即解析參數
    }
}

2. prototype作用域的限制

每次獲取prototype bean都會創建新實例,Spring無法管理這種場景下的循環依賴:

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

實際開發中的建議

最佳實踐

  1. 避免循環依賴:通過設計模式(如事件驅動、觀察者模式)解耦
  2. 使用setter注入替代構造器注入(當必須存在循環依賴時)
  3. 分層清晰:Controller -> Service -> Repository的嚴格分層

常見問題排查

  1. BeanCurrentlyInCreationException

    • 檢查是否是構造器循環依賴
    • 使用-Dspring.main.allow-circular-references=true(Spring Boot 2.6+)
  2. 代理對象異常

    • 確保AOP切面配置正確
    • 檢查@Async等注解的使用

總結

Spring通過三級緩存機制(singletonFactories、earlySingletonObjects、singletonObjects)巧妙地解決了單例Bean的setter/field注入循環依賴問題。其核心思想是:提前暴露未完全初始化的Bean引用。理解這一機制對于:

  1. 深度掌握Spring IoC容器原理
  2. 排查復雜的Bean創建異常
  3. 設計更健壯的應用程序架構

都有重要意義。然而,開發者應當意識到這是框架提供的”安全網”,而非鼓勵循環依賴的設計模式。


附錄:相關源碼類圖

classDiagram
    class DefaultSingletonBeanRegistry{
        +Map<String,Object> singletonObjects
        +Map<String,Object> earlySingletonObjects
        +Map<String,ObjectFactory<?>> singletonFactories
        +getSingleton(String beanName)
        +addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)
    }
    
    class AbstractAutowireCapableBeanFactory{
        +doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args)
        +createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args)
        +populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw)
    }
    
    DefaultSingletonBeanRegistry <|-- AbstractAutowireCapableBeanFactory

(全文約13,300字,實際字數可能因格式調整略有變化) “`

向AI問一下細節

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

AI

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