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

Spring循環(huán)依賴的解決方法

 更新時間:2026年03月11日 09:30:50   作者:程序員碼小跳  
Spring循環(huán)依賴是指兩個或多個Bean相互依賴,形成閉環(huán),Spring通過三級緩存機(jī)制默認(rèn)支持setter注入的循環(huán)依賴,但不支持constructor注入的循環(huán)依賴(會拋異常),本文介紹Spring循環(huán)依賴的解決方法,感興趣的朋友跟隨小編一起看看吧

是什么

Spring循環(huán)依賴指兩個或多個 Bean 相互依賴,形成閉環(huán)。

Spring 通過巧妙的機(jī)制(如三級緩存)默認(rèn)支持 setter 注入的循環(huán)依賴,但不支持 constructor 注入的循環(huán)依賴(會拋異常)。

兩種注入方式

A、構(gòu)造器注入(如 @Autowired 在 constructor 上):容易造成無法解決的循環(huán)依賴,不推薦使用【現(xiàn)象:無法new對象,因為互相是構(gòu)造函數(shù)的入?yún)ⅲ幾g報錯】,會拋 BeanCurrentlyInCreationException。

/**
 * 通過構(gòu)造器的方式注入依賴,構(gòu)造器的方式注入依賴的bean,下面兩個bean循環(huán)依賴
 * 測試后發(fā)現(xiàn),構(gòu)造器循環(huán)依賴是無法解決的
 */
public class ClientConstructor {
    public static void main(String[] args) {
        new ServiceA(new ServiceB(new ServiceA(new ServiceB())));
    }
}

B、Setter 注入:通過 setter 方法或字段注入(如 @Autowired 在字段上),Spring 默認(rèn)支持

@Component
public class A {
    @Autowired
    private B b;  // setter 注入
}
@Component
public class B {
    @Autowired
    private A a;  // setter 注入
}

循環(huán)依賴的解決辦法【重點】

重要結(jié)論:Spring 內(nèi)部通過3級緩存來解決循環(huán)依賴,核心是“提前暴露”半成品 Bean(AOP 代理前),允許其他 Bean 先引用。所謂的三級緩存其實就是 Spring 容器內(nèi)部用來解決循環(huán)依賴問題的三個 Map,這三個 Map 在 DefaultSingletonBeanRegistry 類中。

三級緩存詳解

  1. singletonObjects(一級緩存):我愿稱之為單例池,常說的 Spring 容器就是指它(因為默認(rèn)的bean生命周期是singleton),我們獲取單例 bean 就是在這里面獲取的,存放已經(jīng)經(jīng)歷了完整生命周期的Bean對象【存放完全初始化的 Bean】
  2. earlySingletonObjects(二級緩存):存放早期暴露出來的Bean對象,Bean的生命周期未結(jié)束【存放已經(jīng)實例化,但屬性還未填充完整的半成品 bean】
  3. singletonFactories(三級緩存):存放可以生成Bean的工廠,用于生產(chǎn)(創(chuàng)建)對象【存放FactoryBean工廠池】

創(chuàng)建過程(以 A 依賴 B,B 依賴 A 為例)

  1. 實例化 A:Spring 調(diào)用 getBean(A),創(chuàng)建 A 實例(無屬性),放入三級緩存(singletonFactories,作為 lambda:() -> getEarlyBeanReference(A))。
  2. 注入 A 的屬性:發(fā)現(xiàn)依賴 B,調(diào)用 getBean(B)。
  3. 實例化 B:類似,創(chuàng)建 B 實例,放入三級緩存。
  4. 注入 B 的屬性:發(fā)現(xiàn)依賴 A,從三級緩存獲取 A 的工廠,生成早期 A(半成品),放入二級緩存,并注入到 B。
  5. 完成 B:B 初始化完畢,移到一級緩存。
  6. 返回到 A:A 繼續(xù)注入 B(已完成),完成初始化,移到一級緩存。
  • 如果涉及 AOP:三級緩存的工廠會生成代理對象,確保循環(huán)中代理一致。

getSingleton(): 從容器里面獲得單例的bean,沒有的話則會創(chuàng)建 bean
doCreateBean(): 執(zhí)行創(chuàng)建 bean 的操作(在 Spring 中以 do 開頭的方法都是干實事的方法)
populateBean(): 創(chuàng)建完 bean 之后,對 bean 的屬性進(jìn)行填充
addSingleton(): bean 初始化完成之后,添加到單例容器池中,下次執(zhí)行 getSingleton() 方法時就能獲取到

Bean的創(chuàng)建順序

Bean的創(chuàng)建順序是由BeanDefinition的注冊順序來決定的, 當(dāng)然依賴關(guān)系也會影響B(tài)ean創(chuàng)建順序,比如A依賴B,那肯定B先創(chuàng)建,A后創(chuàng)建。

那BeanDefinition的注冊順序由什么來決定的?
主要是由注解(配置)的解析順序來決定,順序如下:

 @Configuration 
 @Component 
 @Import-類
 @Bean
 @Import—ImportBeanDefinitionRegistrar

總結(jié)

  1. 調(diào)用doGetBean()方法,想要獲取beanA,于是調(diào)用getSingleton()方法從緩存中查找beanA
  2. 在getSingleton()方法中,從一級緩存中查找,沒有,返回null
  3. doGetBean()方法中獲取到的beanA為null,于是走對應(yīng)的處理邏輯,調(diào)用getSingleton()的重載方法(參數(shù)為ObjectFactory的)
  4. 在getSingleton()方法中,先將beanA_name添加到一個集合中,用于標(biāo)記該bean正在創(chuàng)建中。然后回調(diào)匿名內(nèi)部類的creatBean()方法
  5. 進(jìn)入AbstractAutowireCapableBeanFactory#doCreateBean(),先反射調(diào)用構(gòu)造器創(chuàng)建出beanA的實例,然后判斷。是否為單例、是否允許提前暴露引用(對于單例一般為true)、是否正在創(chuàng)建中〈即是否在第四步的集合中)。判斷為true則將beanA添加到【三級緩存】中
  6. 對beanA進(jìn)行屬性填充,此時檢測到beanA依賴于beanB,于是開始查找beanB
  7. 調(diào)用doGetBean()方法,和上面beanA的過程一樣,到緩存中查找beanB,沒有則創(chuàng)建,然后給beanB填充屬性
  8. 此時beanB依賴于beanA,調(diào)用getsingleton()獲取beanA,依次從一級、二級、三級緩存中找,此時從三級緩存中獲取到beanA的創(chuàng)建工廠,通過創(chuàng)建工廠獲取到singletonObject,此時這個singletonObject指向的就是上面在doCreateBean()方法中實例化的bean
  9. 這樣beanB就獲取到了beanA的依賴,于是beanB順利完成實例化,并將beanA從三級緩存移動到二級緩存中
  10. 隨后beanA繼續(xù)他的屬性填充工作,此時也獲取到了beanB,beanA也隨之完成了創(chuàng)建,回到getsingleton()方法中繼續(xù)向下執(zhí)行,將beanA從二級緩存移動到一級緩存中

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

相關(guān)文章

  • Lambda表達(dá)式和Java集合框架

    Lambda表達(dá)式和Java集合框架

    本文主要介紹了Lambda表達(dá)式和Java集合框架的相關(guān)知識,具有很好的參考價值。下面跟著小編一起來看下吧
    2017-03-03
  • SpringBoot?實現(xiàn)流控的操作方法

    SpringBoot?實現(xiàn)流控的操作方法

    本文介紹了限流算法的基本概念和常見的限流算法,包括計數(shù)器算法、漏桶算法和令牌桶算法,還介紹了如何在Spring?Boot中使用Guava庫和自定義注解以及AOP實現(xiàn)接口限流功能,感興趣的朋友一起看看吧
    2024-12-12
  • Java+MySQL前后端連接新手小白教程

    Java+MySQL前后端連接新手小白教程

    Java作為一種廣泛使用的編程語言之一,在開發(fā)Web應(yīng)用程序時經(jīng)常需要連接MySQL數(shù)據(jù)庫進(jìn)行數(shù)據(jù)操作,下面這篇文章主要給大家介紹了關(guān)于Java+MySQL前后端連接的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • 如何使用mybatis-generator自動生成代碼

    如何使用mybatis-generator自動生成代碼

    這篇文章主要介紹了如何使用mybatis-generator自動生成代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • Java 通配符詳解:?、? extends、? super 一篇搞懂

    Java 通配符詳解:?、? extends、? super 一篇搞懂

    本文深入解析Java泛型中的通配符(Wildcard)機(jī)制,重點講解無界通配符(?)、上界通配符(? extends T)和下界通配符(? super T)的用法與區(qū)別,感興趣的可以了解一下
    2025-10-10
  • java二維數(shù)組遍歷的2種代碼

    java二維數(shù)組遍歷的2種代碼

    這篇文章主要介紹了java二維數(shù)組遍歷的2種代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-07-07
  • Mybatis一對多關(guān)聯(lián)關(guān)系映射實現(xiàn)過程解析

    Mybatis一對多關(guān)聯(lián)關(guān)系映射實現(xiàn)過程解析

    這篇文章主要介紹了Mybatis一對多關(guān)聯(lián)關(guān)系映射實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • 詳解SpringCloud-Alibaba-Seata分布式事務(wù)

    詳解SpringCloud-Alibaba-Seata分布式事務(wù)

    這篇文章主要介紹了SpringCloud-Alibaba-Seata分布式事務(wù)的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • 詳解Java中方法next()和nextLine()的區(qū)別與易錯點

    詳解Java中方法next()和nextLine()的區(qū)別與易錯點

    這篇文章主要介紹了詳解Java中方法next()和nextLine()的區(qū)別與易錯點,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • 詳解Spring cloud使用Ribbon進(jìn)行Restful請求

    詳解Spring cloud使用Ribbon進(jìn)行Restful請求

    這篇文章主要介紹了詳解Spring cloud使用Ribbon進(jìn)行Restful請求,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04

最新評論

无极县| 普格县| 无为县| 岳池县| 方正县| 新蔡县| 临泉县| 偏关县| 永善县| 德兴市| 望江县| 抚松县| 永胜县| 临江市| 湾仔区| 望谟县| 宜都市| 井冈山市| 平阳县| 东港市| 绵阳市| 高邮市| 蓬安县| 保定市| 田东县| 太谷县| 赤水市| 女性| 常德市| 纳雍县| 区。| 玉环县| 龙口市| 广南县| 白朗县| 含山县| 通辽市| 东山县| 鄂托克前旗| 清水县| 双江|