最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring中循環(huán)依賴問題的解決機制及詳細流程

 更新時間:2025年08月26日 15:03:50   作者:無糖星軌  
本文給大家介紹Spring中循環(huán)依賴問題的解決機制總結(jié),本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧

一、解決機制

1. 什么是循環(huán)依賴

循環(huán)依賴是指兩個或多個Bean之間相互依賴對方,形成一個閉環(huán)的依賴關(guān)系。最常見的情況是當(dāng)Bean A依賴Bean B,而Bean B又依賴Bean A時,就形成了循環(huán)依賴。在Spring容器初始化過程中,如果不加以特殊處理,這種循環(huán)依賴會導(dǎo)致Bean的創(chuàng)建過程陷入死循環(huán),最終導(dǎo)致應(yīng)用啟動失敗。

例如以下代碼展示了一個典型的循環(huán)依賴場景:

@Service
public class A {
    @Autowired
    private B b;
}
@Service
public class B {
    @Autowired
    private A a;
}

2. Spring三級緩存機制概述

Spring框架通過巧妙的"三級緩存"機制解決了循環(huán)依賴問題。這三級緩存在Spring源碼中是通過三個Map集合實現(xiàn)的:

// 一級緩存:存放完全初始化好的Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二級緩存:存放原始的Bean對象(尚未填充屬性),用于解決循環(huán)依賴
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三級緩存:存放Bean工廠對象,用于解決循環(huán)依賴
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

這三級緩存的作用分別是:

  • 一級緩存(singletonObjects):用于存儲完全初始化好的Bean,即已經(jīng)完成實例化、屬性填充和初始化的Bean,可以直接被其他對象使用。
  • 二級緩存(earlySingletonObjects):用于存儲早期曝光的Bean對象,這些Bean已經(jīng)完成實例化但還未完成屬性填充和初始化。當(dāng)出現(xiàn)循環(huán)依賴時,其他Bean可以引用這個未完全初始化的Bean。
  • 三級緩存(singletonFactories):用于存儲Bean的工廠對象,主要用于處理需要AOP代理的Bean的循環(huán)依賴問題。它存儲的是一個Lambda表達式,這個表達式會返回Bean的早期引用,并在需要時將其放入二級緩存。

3. 三級緩存的查找順序

當(dāng)Spring需要獲取一個單例Bean時,會按照以下順序查找:

  1. 首先從一級緩存(singletonObjects)中查找,如果找到直接返回
  2. 如果一級緩存沒有,再從二級緩存(earlySingletonObjects)中查找
  3. 如果二級緩存也沒有,則從三級緩存(singletonFactories)中查找對應(yīng)的工廠,如果找到則通過工廠獲取對象,并將其放入二級緩存,同時從三級緩存中移除

這種層級查找的機制確保了在循環(huán)依賴的情況下,Bean能夠被正確地創(chuàng)建和注入。

4. Spring解決循環(huán)依賴的詳細流程

以A、B兩個類相互依賴為例,Spring解決循環(huán)依賴的流程如下:

  • 創(chuàng)建Bean A
    • Spring首先創(chuàng)建Bean A的實例(僅完成實例化,未進行屬性填充)
    • 將A的創(chuàng)建工廠放入三級緩存singletonFactories中
    • 開始給A填充屬性,發(fā)現(xiàn)依賴了B
  • 創(chuàng)建Bean B
    • Spring開始創(chuàng)建B(因為A依賴B)
    • 實例化B,并將B的創(chuàng)建工廠放入三級緩存
    • 開始給B填充屬性,發(fā)現(xiàn)依賴了A
  • 處理循環(huán)依賴
    • 此時需要注入A,但A正在創(chuàng)建中
    • Spring嘗試從一級緩存查找A,未找到
    • 繼續(xù)從二級緩存查找A,未找到
    • 最后從三級緩存中找到A的工廠對象
    • 通過工廠獲取A的早期引用(可能是原始對象,也可能是代理對象)
    • 將A的早期引用放入二級緩存,并從三級緩存中移除A的工廠
    • 將A的早期引用注入到B中
  • 完成Bean創(chuàng)建
    • B完成屬性填充和初始化,放入一級緩存
    • 返回到A的屬性填充流程,將B注入到A中
    • A完成屬性填充和初始化,放入一級緩存

通過這個流程,Spring成功解決了循環(huán)依賴問題,關(guān)鍵在于提前暴露了Bean的早期引用。

5. 為什么需要三級緩存

很多人會疑惑,為什么需要三級緩存?二級緩存不能解決循環(huán)依賴問題嗎?

實際上,在不考慮AOP的情況下,二級緩存確實可以解決循環(huán)依賴問題。但Spring設(shè)計三級緩存的主要目的是為了處理AOP代理的情況。

在Spring中,AOP代理是在Bean生命周期的最后階段(初始化后)創(chuàng)建的。但如果出現(xiàn)循環(huán)依賴,就需要提前創(chuàng)建代理對象。三級緩存中的工廠對象可以在需要時(即真正出現(xiàn)循環(huán)依賴時)才創(chuàng)建代理對象,而不是對所有Bean都提前創(chuàng)建代理。

這種設(shè)計有以下優(yōu)勢:

  1. 延遲代理對象的創(chuàng)建:只有在真正需要時才創(chuàng)建代理對象,避免了不必要的性能開銷
  2. 保持Bean生命周期的一致性:盡可能地保持Bean的標(biāo)準生命周期流程
  3. 靈活處理各種代理場景:適應(yīng)不同的AOP實現(xiàn)和代理方式

6. 三級緩存的局限性

雖然三級緩存機制能夠解決大多數(shù)循環(huán)依賴問題,但它仍有一些局限性:

  • 不能解決構(gòu)造器注入的循環(huán)依賴:因為構(gòu)造器注入發(fā)生在實例化階段,此時Bean還未被放入三級緩存,所以無法解決
  • 不能解決prototype作用域的循環(huán)依賴:三級緩存機制只對單例Bean有效,對于prototype作用域的Bean,Spring不會緩存其實例,因此無法解決其循環(huán)依賴
  • 不能解決多例Bean之間的循環(huán)依賴:原因同上,Spring不會緩存非單例Bean

7. 源碼分析

Spring解決循環(huán)依賴的核心源碼主要在DefaultSingletonBeanRegistry類中,關(guān)鍵方法包括:

  1. getSingleton(String beanName):按照一級、二級、三級緩存的順序查找Bean
  2. addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory):將Bean的工廠對象放入三級緩存
  3. doGetBean(String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly):獲取Bean的主要邏輯

AbstractAutowireCapableBeanFactorydoCreateBean方法中,會在Bean實例化后立即將其工廠對象放入三級緩存,為解決循環(huán)依賴做準備。

8. 總結(jié)

Spring通過三級緩存機制巧妙地解決了循環(huán)依賴問題,其核心思想是將Bean的實例化和初始化分離,提前暴露實例化但未完全初始化的對象。三級緩存的設(shè)計不僅解決了基本的循環(huán)依賴問題,還優(yōu)雅地處理了AOP代理場景下的循環(huán)依賴。

這種機制體現(xiàn)了Spring框架設(shè)計的精妙之處,通過緩存分層和提前暴露對象的方式,在不影響B(tài)ean正常生命周期的前提下解決了看似棘手的循環(huán)依賴問題。

二、實際示例與解析

為了更直觀地理解Spring如何通過三級緩存解決循環(huán)依賴問題,我們來看一個具體的示例,并詳細分析整個過程。

1. 示例代碼

首先,我們創(chuàng)建兩個相互依賴的Service類:

@Service
public class UserService {
    @Autowired
    private OrderService orderService;
    public void findUserOrders(Long userId) {
        System.out.println("查詢用戶訂單");
        orderService.getOrdersByUserId(userId);
    }
}
@Service
public class OrderService {
    @Autowired
    private UserService userService;
    public List<Order> getOrdersByUserId(Long userId) {
        System.out.println("獲取用戶訂單");
        return new ArrayList<>();
    }
    public void notifyUser(Long orderId) {
        System.out.println("通知用戶訂單狀態(tài)");
        userService.findUserOrders(1L); // 調(diào)用UserService的方法
    }
}

在這個示例中,UserService依賴OrderService,而OrderService又依賴UserService,形成了典型的循環(huán)依賴。

2. 詳細解析Spring處理流程

讓我們詳細分析Spring如何處理這個循環(huán)依賴:

2.1 創(chuàng)建UserService

當(dāng)Spring容器啟動時,會按照Bean定義順序開始創(chuàng)建Bean。假設(shè)先創(chuàng)建UserService

1. 實例化UserService對象(僅調(diào)用構(gòu)造函數(shù),此時內(nèi)部屬性尚未賦值)
2. 將UserService實例的創(chuàng)建工廠添加到三級緩存(singletonFactories)中
   singletonFactories.put("userService", () -> getEarlyBeanReference(beanName, mbd, userService實例))
3. 開始填充UserService的屬性,發(fā)現(xiàn)需要注入OrderService

2.2 創(chuàng)建OrderService

由于UserService依賴OrderService,Spring開始創(chuàng)建OrderService:

1. 實例化OrderService對象(僅調(diào)用構(gòu)造函數(shù))
2. 將OrderService實例的創(chuàng)建工廠添加到三級緩存中
   singletonFactories.put("orderService", () -> getEarlyBeanReference(beanName, mbd, orderService實例))
3. 開始填充OrderService的屬性,發(fā)現(xiàn)需要注入UserService

2.3 處理循環(huán)依賴

此時出現(xiàn)了關(guān)鍵的循環(huán)依賴處理步驟:

1. OrderService需要注入UserService,但UserService正在創(chuàng)建中
2. Spring嘗試從一級緩存(singletonObjects)查找UserService,未找到
3. 繼續(xù)從二級緩存(earlySingletonObjects)查找UserService,未找到
4. 最后從三級緩存(singletonFactories)中找到UserService的工廠對象
5. 調(diào)用工廠對象的getObject()方法獲取UserService的早期引用
   - 如果UserService需要被代理(如有@Transactional注解),此時會創(chuàng)建代理對象
   - 如果不需要代理,則返回原始對象
6. 將獲取到的UserService早期引用放入二級緩存,同時從三級緩存中移除
   earlySingletonObjects.put("userService", userService早期引用)
   singletonFactories.remove("userService")
7. 將UserService的早期引用注入到OrderService中

2.4 完成Bean創(chuàng)建

接下來完成兩個Bean的創(chuàng)建過程:

1. OrderService完成屬性填充(已注入UserService的早期引用)
2. OrderService完成初始化(調(diào)用各種初始化方法)
3. 如果OrderService需要被代理,創(chuàng)建代理對象(AOP)
4. 將完全初始化好的OrderService放入一級緩存
   singletonObjects.put("orderService", 完全初始化的orderService)
5. 返回到UserService的屬性填充流程,將完全初始化好的OrderService注入到UserService中
6. UserService完成初始化
7. 如果UserService需要被代理且之前沒有提前創(chuàng)建代理,創(chuàng)建代理對象
8. 將完全初始化好的UserService放入一級緩存
   singletonObjects.put("userService", 完全初始化的userService)

3. 關(guān)鍵源碼執(zhí)行分析

讓我們看一下在這個過程中涉及的關(guān)鍵源碼執(zhí)行流程:

3.1 從getSingleton方法開始

當(dāng)Spring嘗試獲取一個Bean時,首先會調(diào)用getSingleton方法:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 首先從一級緩存查找
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果一級緩存沒有,且該Bean正在創(chuàng)建中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 從二級緩存查找
        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.2 添加Bean到三級緩存

在Bean實例化后,Spring會將其添加到三級緩存中:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        // 如果一級緩存中不存在
        if (!this.singletonObjects.containsKey(beanName)) {
            // 添加到三級緩存
            this.singletonFactories.put(beanName, singletonFactory);
            // 確保二級緩存中不存在
            this.earlySingletonObjects.remove(beanName);
            // 記錄注冊的單例
            this.registeredSingletons.add(beanName);
        }
    }
}

3.3 創(chuàng)建早期引用

當(dāng)需要處理循環(huán)依賴時,Spring會通過getEarlyBeanReference方法獲取早期引用:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = 
                    (SmartInstantiationAwareBeanPostProcessor) bp;
                // 這里可能創(chuàng)建代理對象
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

4. AOP場景下的循環(huán)依賴

特別值得注意的是,當(dāng)涉及到AOP代理時,循環(huán)依賴的處理會更加復(fù)雜。例如,如果我們的示例中添加了事務(wù)注解:

@Service
@Transactional
public class UserService {
    // ...
}
@Service
@Transactional
public class OrderService {
    // ...
}

在這種情況下:

  1. 當(dāng)從三級緩存獲取UserService的早期引用時,會通過AbstractAutoProxyCreator.getEarlyBeanReference方法創(chuàng)建代理對象
  2. 這個代理對象會被放入二級緩存,并最終注入到OrderService中
  3. 這就確保了OrderService依賴的是UserService的代理對象,而不是原始對象

這也解釋了為什么需要三級緩存而不是二級緩存:三級緩存中存儲的工廠可以在需要時(出現(xiàn)循環(huán)依賴時)才創(chuàng)建代理對象,而不是對所有Bean都提前創(chuàng)建代理。

5. 總結(jié)

通過這個實際示例,我們可以清晰地看到Spring如何通過三級緩存機制解決循環(huán)依賴問題:

  1. 實例化Bean后立即將其工廠對象放入三級緩存
  2. 當(dāng)出現(xiàn)循環(huán)依賴時,通過三級緩存獲取早期引用(可能是代理對象)
  3. 將早期引用放入二級緩存,并從三級緩存中移除
  4. 使用早期引用完成依賴注入
  5. 最終將完全初始化的Bean放入一級緩存

這種機制既解決了循環(huán)依賴問題,又保持了Spring Bean生命周期的完整性,同時還能靈活處理AOP代理場景,體現(xiàn)了Spring框架設(shè)計的精妙之處。

到此這篇關(guān)于Spring中循環(huán)依賴問題的解決機制總結(jié)的文章就介紹到這了,更多相關(guān)Spring循環(huán)依賴內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java版本怎么選以及JDK各版本特性對比與實戰(zhàn)建議

    Java版本怎么選以及JDK各版本特性對比與實戰(zhàn)建議

    Java是一種廣泛使用的編程語言,擁有一個龐大的社區(qū)和大量的生態(tài)系統(tǒng),自從Java的早期版本以來,它已經(jīng)經(jīng)歷了許多變化和改進,這篇文章主要介紹了Java版本怎么選以及JDK各版本特性對比與實戰(zhàn)建議的相關(guān)資料,需要的朋友可以參考下
    2025-11-11
  • java求整數(shù)的位數(shù)方式

    java求整數(shù)的位數(shù)方式

    這篇文章主要介紹了java求整數(shù)的位數(shù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java8優(yōu)雅的字符串拼接工具類StringJoiner實例代碼

    Java8優(yōu)雅的字符串拼接工具類StringJoiner實例代碼

    這篇文章主要給大家介紹了關(guān)于Java8優(yōu)雅的字符串拼接工具類StringJoiner的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • Lombok中@EqualsAndHashCode注解的使用及說明

    Lombok中@EqualsAndHashCode注解的使用及說明

    這篇文章主要介紹了Lombok中@EqualsAndHashCode注解的使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • LeetCode程序員面試題之遞歸乘法

    LeetCode程序員面試題之遞歸乘法

    在Java中,遞歸乘法是一種簡單而有效的方法,可以用來計算兩個數(shù)字的乘積。它的基本思想是:如果第一個數(shù)字是0,則乘積為0;如果第一個數(shù)字是1,則乘積為第二個數(shù)字;其他情況,則通過將第一個數(shù)字減1,并將第二個數(shù)字與自身相乘,來實現(xiàn)遞歸乘法。
    2023-02-02
  • java實現(xiàn)的根據(jù)概率隨機中獎測試類

    java實現(xiàn)的根據(jù)概率隨機中獎測試類

    這篇文章主要介紹了java實現(xiàn)的根據(jù)概率隨機中獎測試類,結(jié)合完整實例形式詳細分析了java隨機數(shù)實現(xiàn)概率運算相關(guān)操作技巧,需要的朋友可以參考下
    2019-09-09
  • Spring Data Redis對象緩存序列化問題解決

    Spring Data Redis對象緩存序列化問題解決

    相信在項目中,你一定是經(jīng)常使用Redis,在使用時,有沒有遇到同我一樣,對象緩存序列化問題的呢,本文主要介紹了Spring Data Redis對象緩存序列化問題解決,感興趣的可以了解一下
    2024-01-01
  • springboot+dubbo實現(xiàn)時間輪算法

    springboot+dubbo實現(xiàn)時間輪算法

    時間輪是一種高效利用線程資源進行批量化調(diào)度的算法,本文主要介紹了springboot+dubbo實現(xiàn)時間輪算法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-04-04
  • SpringBoot整合Mybatis與druid實現(xiàn)流程詳解

    SpringBoot整合Mybatis與druid實現(xiàn)流程詳解

    這篇文章主要介紹了springboot整合mybatis plus與druid詳情,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的下伙伴可以參考一下
    2022-10-10
  • SpringBoot整合RabbitMQ處理死信隊列和延遲隊列

    SpringBoot整合RabbitMQ處理死信隊列和延遲隊列

    這篇文章將通過示例為大家詳細介紹SpringBoot整合RabbitMQ時如何處理死信隊列和延遲隊列,文中的示例代碼講解詳細,需要的可以參考一下
    2022-05-05

最新評論

衡南县| 孟州市| 社会| 华容县| 赫章县| 西和县| 肃宁县| 库尔勒市| 彰武县| 于田县| 葫芦岛市| 龙川县| 肥东县| 肇州县| 合作市| 大田县| 香格里拉县| 乌恰县| 九江市| 乌鲁木齐县| 涟水县| 嘉禾县| 北京市| 广德县| 佛坪县| 九江县| 台前县| 会理县| 斗六市| 杭锦旗| 武夷山市| 隆昌县| 岚皋县| 朔州市| 天长市| 太康县| 象州县| 淳化县| 华蓥市| 靖西县| 靖安县|