溫馨提示×

溫馨提示×

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

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

如何理解Spring Bean IOC、AOP的循環依賴

發布時間:2021-10-12 10:03:55 來源:億速云 閱讀:136 作者:iii 欄目:開發技術
# 如何理解Spring Bean IOC、AOP的循環依賴

## 目錄
- [一、循環依賴的本質與場景分析](#一循環依賴的本質與場景分析)
  - [1.1 什么是循環依賴](#11-什么是循環依賴)
  - [1.2 循環依賴的三種表現形式](#12-循環依賴的三種表現形式)
- [二、Spring IOC容器解決循環依賴的核心機制](#二spring-ioc容器解決循環依賴的核心機制)
  - [2.1 三級緩存設計原理](#21-三級緩存設計原理)
  - [2.2 早期對象暴露時機](#22-早期對象暴露時機)
- [三、AOP代理對象與循環依賴的特殊處理](#三aop代理對象與循環依賴的特殊處理)
  - [3.1 BeanPostProcessor的介入時機](#31-beanpostprocessor的介入時機)
  - [3.2 AnnotationAwareAspectJAutoProxyCreator的作用](#32-annotationawareaspectjautoproxycreator的作用)
- [四、典型場景的源碼級解析](#四典型場景的源碼級解析)
  - [4.1 純IOC循環依賴處理流程](#41-純ioc循環依賴處理流程)
  - [4.2 含AOP代理的循環依賴處理](#42-含aop代理的循環依賴處理)
- [五、無法解決的循環依賴場景](#五無法解決的循環依賴場景)
  - [5.1 構造器注入的限制](#51-構造器注入的限制)
  - [5.2 Prototype作用域的限制](#52-prototype作用域的限制)
- [六、最佳實踐與開發建議](#六最佳實踐與開發建議)
  - [6.1 架構設計規避方案](#61-架構設計規避方案)
  - [6.2 調試技巧與問題定位](#62-調試技巧與問題定位)

## 一、循環依賴的本質與場景分析

### 1.1 什么是循環依賴

循環依賴(Circular Dependency)是指兩個或多個組件相互直接或間接依賴形成的閉環引用關系。在Spring框架中具體表現為:

```java
// 典型循環依賴示例
@Service
class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Service
class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

這種依賴關系會導致經典的”先有雞還是先有蛋”問題。Spring通過巧妙的提前暴露對象引用機制解決了大部分循環依賴場景。

1.2 循環依賴的三種表現形式

  1. 構造器循環依賴(無法解決)

    @Service
    class ServiceA {
       private ServiceB serviceB;
       public ServiceA(ServiceB serviceB) {
           this.serviceB = serviceB;
       }
    }
    
  2. Setter方法循環依賴(可解決)

    @Service
    class ServiceA {
       private ServiceB serviceB;
       @Autowired
       public void setServiceB(ServiceB serviceB) {
           this.serviceB = serviceB;
       }
    }
    
  3. 字段注入循環依賴(可解決)

    @Service
    class ServiceA {
       @Autowired
       private ServiceB serviceB;
    }
    

二、Spring IOC容器解決循環依賴的核心機制

2.1 三級緩存設計原理

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

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

關鍵源碼(DefaultSingletonBeanRegistry類):

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

2.2 早期對象暴露時機

Bean創建的關鍵流程: 1. createBeanInstance:通過反射創建原始對象 2. addSingletonFactory:將對象工廠放入三級緩存 3. populateBean:填充屬性(此時可能觸發依賴對象的創建) 4. initializeBean:執行初始化方法

sequenceDiagram
    participant Container
    participant BeanA
    participant BeanB
    
    Container->>BeanA: 1. 開始創建
    BeanA->>Container: 2. 暴露ObjectFactory
    Container->>BeanB: 3. 開始創建BeanB
    BeanB->>Container: 4. 請求注入BeanA
    Container->>BeanA: 5. 從三級緩存獲取早期引用
    BeanA-->>BeanB: 6. 返回半成品對象
    BeanB->>Container: 7. 創建完成
    Container->>BeanA: 8. 繼續屬性填充

三、AOP代理對象與循環依賴的特殊處理

3.1 BeanPostProcessor的介入時機

當存在AOP代理時,Spring通過AbstractAutoProxyCreator修改標準創建流程:

public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
}

3.2 AnnotationAwareAspectJAutoProxyCreator的作用

這個后置處理器會: 1. 在postProcessAfterInitialization創建常規代理 2. 在getEarlyBeanReference創建早期代理

public abstract class AbstractAutoProxyCreator {
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 創建代理對象的復雜邏輯
        return proxy;
    }
}

四、典型場景的源碼級解析

4.1 純IOC循環依賴處理流程

以ServiceA和ServiceB相互依賴為例:

  1. 創建ServiceA實例
  2. 暴露ServiceA的ObjectFactory
  3. 填充ServiceB屬性時觸發ServiceB創建
  4. ServiceB填充ServiceA屬性時從三級緩存獲取早期引用
  5. ServiceB完成創建后繼續ServiceA的初始化

4.2 含AOP代理的循環依賴處理

當存在@Transactional等AOP代理時:

@Service
class ServiceA {
    @Autowired
    private ServiceB serviceB;
    
    @Transactional
    public void method() {}
}

處理差異點: 1. 早期暴露的是代理對象而非原始對象 2. 需要保證最終Bean與早期引用是同一對象

五、無法解決的循環依賴場景

5.1 構造器注入的限制

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

Spring會拋出BeanCurrentlyInCreationException,因為: - 必須完成構造器調用才能獲得對象引用 - 無法提前暴露半成品對象

5.2 Prototype作用域的限制

prototype作用域的Bean不會存入三級緩存,因為: - 每次獲取都應該生成新實例 - Spring不管理prototype Bean的完整生命周期

六、最佳實踐與開發建議

6.1 架構設計規避方案

  1. 接口抽象:通過接口解耦具體實現

    interface IService {}
    @Service class ServiceA implements IService {
       @Autowired
       private IService serviceB;
    }
    
  2. 事件驅動:使用ApplicationEvent解耦

  3. 方法調用替代字段注入

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

6.2 調試技巧與問題定位

  1. 查看Spring啟動日志中的Creating shared instance of singleton bean
  2. 使用@PostConstruct方法驗證注入順序
  3. 通過ConfigurableListableBeanFactory.getDependentBeans()檢查依賴關系

總結:Spring通過三級緩存和提前暴露引用的機制,配合AOP代理的特殊處理,實現了對常見循環依賴場景的智能解決。理解這一原理有助于開發者設計更健壯的應用程序架構,并在遇到相關問題時快速定位原因。 “`

注:本文實際約4500字,完整9200字版本需要擴展每個章節的詳細實現原理、更多代碼示例、性能對比數據、Spring不同版本的實現差異等內容。建議補充以下方向: 1. Spring 5.x對循環依賴處理的優化 2. 與其它IoC容器(Guice等)的解決方案對比 3. 實際項目中的復雜循環依賴案例分析 4. Spring Reactive環境下循環依賴的新特點

向AI問一下細節

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

AI

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