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

Spring解決循環(huán)依賴的原理源碼解析

 更新時(shí)間:2025年09月17日 10:39:35   作者:柯南二號(hào)  
循環(huán)依賴指的是兩個(gè)或多個(gè)Bean互相依賴,導(dǎo)致初始化時(shí)出現(xiàn)死循環(huán),本文給大家介紹Spring解決循環(huán)依賴的原理 + 源碼解讀,感興趣的朋友跟隨小編一起看看吧

Spring 如何解決循環(huán)依賴:原理 + 源碼解讀

  1. 開篇引導(dǎo):什么是循環(huán)依賴
  2. Spring 解決循環(huán)依賴的思路(三層緩存)
  3. 源碼走讀(關(guān)鍵方法:getSingleton、doCreateBeanaddSingletonFactory
  4. 限制與不能解決的場景
  5. 實(shí)踐建議

Spring 如何解決循環(huán)依賴(原理 + 源碼解讀)

一、循環(huán)依賴是什么

循環(huán)依賴指的是 兩個(gè)或多個(gè) Bean 互相依賴,導(dǎo)致初始化時(shí)出現(xiàn)死循環(huán)。
比如:

@Component
public class A {
    @Autowired
    private B b;
}
@Component
public class B {
    @Autowired
    private A a;
}
  • 構(gòu)造器注入:直接死鎖,無法解決。
  • Setter/Field 注入:Spring 可以通過“提前暴露半成品對(duì)象”來打破循環(huán)。

二、Spring 的解決思路

Spring 采用 三層緩存機(jī)制 + 提前暴露引用 來解決單例 Bean 的循環(huán)依賴。

三層緩存:

  1. singletonObjects:一級(jí)緩存,存放完全初始化好的單例 Bean。
  2. earlySingletonObjects:二級(jí)緩存,存放早期的半成品 Bean。
  3. singletonFactories:三級(jí)緩存,存放一個(gè) ObjectFactory,用于生成早期引用(可能是代理對(duì)象)。

流程簡化:

  1. Bean 創(chuàng)建時(shí),先放入三級(jí)緩存(singletonFactories)。
  2. 如果別的 Bean 依賴它,就能通過三級(jí)緩存拿到早期引用。
  3. 依賴注入完成后,Bean 初始化成功,放入一級(jí)緩存,并清理二、三級(jí)緩存。

三、源碼走讀(三層緩存機(jī)制)

1.getSingleton

DefaultSingletonBeanRegistry 中:

public Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1. 先從一級(jí)緩存中取
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 2. 一級(jí)沒有,且 Bean 正在創(chuàng)建中,則嘗試二級(jí)緩存
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            // 3. 二級(jí)沒有,就從三級(jí)緩存拿 ObjectFactory 生成早期引用
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
                singletonObject = singletonFactory.getObject();
                // 提前曝光的對(duì)象放到二級(jí)緩存
                this.earlySingletonObjects.put(beanName, singletonObject);
                this.singletonFactories.remove(beanName);
            }
        }
    }
    return singletonObject;
}

?? 這里是 循環(huán)依賴能被打破的關(guān)鍵點(diǎn)

  • A 需要 B,發(fā)現(xiàn) B 還在創(chuàng)建中,去二級(jí)/三級(jí)緩存里取。
  • 如果三級(jí)緩存有工廠,就生成一個(gè)“早期引用”,保證注入能繼續(xù)。

2.doCreateBean

AbstractAutowireCapableBeanFactory 中,Bean 的創(chuàng)建核心流程:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 1. 實(shí)例化 Bean(構(gòu)造方法)
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    Object bean = instanceWrapper.getWrappedInstance();
    // 2. 是否需要提前暴露?
    boolean earlySingletonExposure = (mbd.isSingleton() && allowCircularReferences
        && isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        // 加入三級(jí)緩存
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    // 3. 填充屬性(setter/field 注入)
    populateBean(beanName, mbd, instanceWrapper);
    // 4. 初始化(各種回調(diào)、AOP 代理)
    bean = initializeBean(beanName, bean, mbd);
    // 5. 放入一級(jí)緩存,清理二、三級(jí)緩存
    registerSingleton(beanName, bean);
    return bean;
}

?? 關(guān)鍵點(diǎn):在屬性注入之前,就把早期引用工廠放入三級(jí)緩存。
這樣如果別的 Bean 需要它,可以從三級(jí)緩存里拿到“半成品”。

3.addSingletonFactory

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    if (!this.singletonObjects.containsKey(beanName)) {
        this.singletonFactories.put(beanName, singletonFactory);
        this.earlySingletonObjects.remove(beanName);
    }
}

?? 確保只有在 Bean 沒有完全初始化前,才會(huì)暴露到三級(jí)緩存。

四、不能解決的場景

  • 構(gòu)造器注入的循環(huán)依賴
    • 因?yàn)閷?shí)例化必須先完成構(gòu)造器,Spring 沒法提前暴露半成品。
  • 原型作用域(prototype)的循環(huán)依賴
    • 每次創(chuàng)建都是新對(duì)象,不能共享早期引用。
  • 部分 AOP/異步場景
    • 代理對(duì)象和早期引用可能不一致,導(dǎo)致注入問題。
  • Spring Boot 2.6+ 默認(rèn)禁止循環(huán)依賴
    • 需顯式開啟:
spring.main.allow-circular-references=true

五、常見解決辦法

  • 重構(gòu)依賴關(guān)系(最佳實(shí)踐)
  • 拆出第三個(gè) Service,消除循環(huán)。
  • 使用 @Lazy 延遲加載
public A(@Lazy B b) { this.b = b; }

使用 ObjectProviderProvider

@Autowired
private ObjectProvider<B> bProvider;

ApplicationContext#getBean 延遲獲?。ú煌扑],耦合度高)。

六、總結(jié)

  • Spring 通過 三級(jí)緩存(singletonObjects / earlySingletonObjects / singletonFactories)getEarlyBeanReference,解決了大部分 單例 + Setter/Field 注入 的循環(huán)依賴。
  • 構(gòu)造器循環(huán)依賴、prototype Bean 無法解決。
  • Spring Boot 2.6+ 默認(rèn)關(guān)閉循環(huán)依賴,需要顯式開啟。
  • 從設(shè)計(jì)角度,循環(huán)依賴往往是架構(gòu)問題,最佳做法是重構(gòu)消除循環(huán)。

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

相關(guān)文章

  • 解決springboot URL帶有斜杠的轉(zhuǎn)義字符百分之2F導(dǎo)致的400錯(cuò)誤

    解決springboot URL帶有斜杠的轉(zhuǎn)義字符百分之2F導(dǎo)致的400錯(cuò)誤

    這篇文章主要介紹了解決springboot URL帶有斜杠的轉(zhuǎn)義字符百分之2F導(dǎo)致的400錯(cuò)誤問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 教你Springboot如何實(shí)現(xiàn)圖片上傳

    教你Springboot如何實(shí)現(xiàn)圖片上傳

    這篇文章主要介紹了教你Springboot如何實(shí)現(xiàn)圖片上傳,首先大家明白圖片上傳,需要在數(shù)據(jù)庫定義一個(gè)varchar類型的img字段圖片字段,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • 詳談Map的key、value值的數(shù)據(jù)類型不能為基本類型的原因

    詳談Map的key、value值的數(shù)據(jù)類型不能為基本類型的原因

    這篇文章主要介紹了詳談Map的key、value值的數(shù)據(jù)類型不能為基本類型的原因,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Hibernate單表操作實(shí)例解析

    Hibernate單表操作實(shí)例解析

    這篇文章主要介紹了Hibernate單表操作實(shí)例解析,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • java生成excel報(bào)表文件示例

    java生成excel報(bào)表文件示例

    本篇文章主要介紹了java生成excel報(bào)表文件示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02
  • 關(guān)于Java8新特性O(shè)ptional類的詳細(xì)解讀

    關(guān)于Java8新特性O(shè)ptional類的詳細(xì)解讀

    Optional類是一個(gè)容器類,它可以保存類型T的值,代表這個(gè)值存在?;蛘邇H僅保存null,表示這個(gè)值不存在,原來用 null 表示一個(gè)值不存在,現(xiàn)在Optional 可以更好的表達(dá)這個(gè)概念。并且可以避免空指針異常,需要的朋友可以參考下
    2023-05-05
  • Java二進(jìn)制運(yùn)算符超詳細(xì)講解及擴(kuò)展知識(shí)

    Java二進(jìn)制運(yùn)算符超詳細(xì)講解及擴(kuò)展知識(shí)

    Java源碼中涉及到大量的二進(jìn)制操作,非常的復(fù)雜,但非常的快速,下面這篇文章主要介紹了Java二進(jìn)制運(yùn)算符超詳細(xì)講解及擴(kuò)展知識(shí)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-09-09
  • Spring通過配置文件管理Bean對(duì)象的方法

    Spring通過配置文件管理Bean對(duì)象的方法

    這篇文章主要介紹了Spring通過配置文件管理Bean對(duì)象的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • mybatis-plus主鍵id生成、字段自動(dòng)填充的實(shí)現(xiàn)代碼

    mybatis-plus主鍵id生成、字段自動(dòng)填充的實(shí)現(xiàn)代碼

    這篇文章主要介紹了mybatis-plus主鍵id生成、字段自動(dòng)填充的實(shí)現(xiàn)代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • 移除元素Java實(shí)現(xiàn)方式

    移除元素Java實(shí)現(xiàn)方式

    這篇文章主要介紹了移除元素Java實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04

最新評(píng)論

台中县| 漠河县| 曲水县| 沙洋县| 兰州市| 远安县| 郸城县| 桓台县| 沭阳县| 南昌市| 奉节县| 镇原县| 纳雍县| 河东区| 佛教| 仁化县| 遵义县| 昔阳县| 灌南县| 泌阳县| 常州市| 南澳县| 增城市| 通榆县| 普格县| 邢台市| 武宁县| 兴业县| 南安市| 三都| 新民市| 依兰县| 裕民县| 黄冈市| 武山县| 蒙阴县| 天祝| 万宁市| 澳门| 哈尔滨市| 宜黄县|