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

Spring用三級(jí)緩存處理循環(huán)依賴的方法詳解

 更新時(shí)間:2025年02月03日 08:50:53   作者:時(shí)雨h  
這篇文章主要介紹了Spring用三級(jí)緩存處理循環(huán)依賴的方法,在Spring?框架中,依賴注入是其核心特性之一,它允許對(duì)象之間的依賴關(guān)系在運(yùn)行時(shí)動(dòng)態(tài)注入,然而,當(dāng)多個(gè)Bean之間的依賴關(guān)系形成一個(gè)閉環(huán)時(shí),就會(huì)出現(xiàn)循環(huán)依賴問題,本文就為解決此問題,需要的朋友可以參考下

循環(huán)依賴問題的本質(zhì)

循環(huán)依賴的定義與常見場景

在 Spring 框架中,依賴注入是其核心特性之一,它允許對(duì)象之間的依賴關(guān)系在運(yùn)行時(shí)動(dòng)態(tài)注入。然而,當(dāng)多個(gè) Bean 之間的依賴關(guān)系形成一個(gè)閉環(huán)時(shí),就會(huì)出現(xiàn)循環(huán)依賴問題。簡單來說,循環(huán)依賴就是 Bean A 依賴于 Bean B,而 Bean B 又依賴于 Bean A,或者存在更復(fù)雜的多 Bean 循環(huán)依賴鏈。

常見的循環(huán)依賴場景包括構(gòu)造器注入循環(huán)依賴和屬性注入循環(huán)依賴。下面分別通過代碼示例來展示這兩種場景。

構(gòu)造器注入循環(huán)依賴

@Component
public class ConstructorBeanA {
    private ConstructorBeanB beanB;
    @Autowired
    public ConstructorBeanA(ConstructorBeanB beanB) {
        this.beanB = beanB;
    }
}
@Component
public class ConstructorBeanB {
    private ConstructorBeanA beanA;
    @Autowired
    public ConstructorBeanB(ConstructorBeanA beanA) {
        this.beanA = beanA;
    }
}

在上述代碼中,ConstructorBeanA 通過構(gòu)造器依賴于 ConstructorBeanB,而 ConstructorBeanB 同樣通過構(gòu)造器依賴于 ConstructorBeanA。當(dāng) Spring 容器嘗試創(chuàng)建這兩個(gè) Bean 時(shí),會(huì)陷入無限循環(huán),因?yàn)閯?chuàng)建 ConstructorBeanA 需要先創(chuàng)建 ConstructorBeanB,而創(chuàng)建 ConstructorBeanB 又需要先創(chuàng)建 ConstructorBeanA。

屬性注入循環(huán)依賴

@Component
public class PropertyBeanA {
    @Autowired
    private PropertyBeanB beanB;
}
@Component
public class PropertyBeanB {
    @Autowired
    private PropertyBeanA beanA;
}

在屬性注入循環(huán)依賴的場景中,PropertyBeanA 通過屬性注入依賴于 PropertyBeanB,PropertyBeanB 也通過屬性注入依賴于 PropertyBeanA。雖然屬性注入的循環(huán)依賴可以通過 Spring 的三級(jí)緩存機(jī)制解決,但構(gòu)造器注入的循環(huán)依賴無法通過該機(jī)制解決,這是因?yàn)闃?gòu)造器注入要求在 Bean 實(shí)例化時(shí)就完成依賴注入,而此時(shí) Bean 還未進(jìn)入可以暴露的階段。

循環(huán)依賴帶來的問題

循環(huán)依賴會(huì)導(dǎo)致 Spring 容器在創(chuàng)建 Bean 時(shí)陷入無限循環(huán),最終拋出 BeanCurrentlyInCreationException 異常。這是因?yàn)樵跊]有合適解決方案的情況下,Spring 會(huì)不斷嘗試創(chuàng)建相互依賴的 Bean,無法完成 Bean 的初始化過程。例如,當(dāng)創(chuàng)建 ConstructorBeanA 時(shí),需要調(diào)用其構(gòu)造器并傳入 ConstructorBeanB 的實(shí)例,而此時(shí) ConstructorBeanB 還未創(chuàng)建,于是 Spring 開始創(chuàng)建 ConstructorBeanB。但在創(chuàng)建 ConstructorBeanB 時(shí),又需要 ConstructorBeanA 的實(shí)例,這樣就會(huì)陷入死循環(huán),導(dǎo)致程序無法正常運(yùn)行。

三級(jí)緩存的構(gòu)成與作用

一級(jí)緩存-完全初始化Bean的倉庫

一級(jí)緩存的定義與數(shù)據(jù)結(jié)構(gòu)

一級(jí)緩存,也稱為單例池,在 Spring 中通常用 ConcurrentHashMap<String, Object> 來實(shí)現(xiàn)。其中,鍵是 Bean 的名稱,值是已經(jīng)完全初始化好的 Bean 實(shí)例。ConcurrentHashMap 是線程安全的,這保證了在多線程環(huán)境下對(duì)一級(jí)緩存的訪問和操作是安全的。

// 一級(jí)緩存的示例定義
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

一級(jí)緩存的作用

一級(jí)緩存是 Spring 中最終存儲(chǔ)可用 Bean 的地方。當(dāng)一個(gè) Bean 經(jīng)歷了所有的初始化步驟,包括實(shí)例化、屬性注入、初始化方法調(diào)用等,最終成為一個(gè)可以直接使用的完整 Bean 時(shí),它會(huì)被放入一級(jí)緩存中。后續(xù)其他 Bean 如果需要依賴這個(gè) Bean,就可以直接從一級(jí)緩存中獲取,避免了重復(fù)創(chuàng)建 Bean 的開銷。例如,當(dāng)一個(gè)服務(wù)層的 Bean 需要依賴一個(gè)數(shù)據(jù)訪問層的 Bean 時(shí),Spring 會(huì)首先從一級(jí)緩存中查找該數(shù)據(jù)訪問層 Bean,如果存在則直接使用。

一級(jí)緩存的使用時(shí)機(jī)

在 Bean 創(chuàng)建的最后階段,當(dāng) Bean 完成了所有的初始化操作并且通過了各種后置處理器的處理后,Spring 會(huì)將該 Bean 放入一級(jí)緩存中。同時(shí),會(huì)從二級(jí)緩存和三級(jí)緩存中移除該 Bean 的相關(guān)信息,以確保 Bean 只在一級(jí)緩存中存在。

二級(jí)緩存-早期暴露的Bean臨時(shí)存儲(chǔ)

二級(jí)緩存的定義與數(shù)據(jù)結(jié)構(gòu)

二級(jí)緩存也是一個(gè) Map 結(jié)構(gòu),通常使用 ConcurrentHashMap<String, Object> 實(shí)現(xiàn)。它用于存放早期暴露的 Bean 對(duì)象,這些 Bean 已經(jīng)完成了實(shí)例化,但還沒有進(jìn)行屬性填充等后續(xù)操作。

// 二級(jí)緩存的示例定義
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

二級(jí)緩存的作用

二級(jí)緩存的主要作用是在解決循環(huán)依賴時(shí),提供一個(gè)臨時(shí)存儲(chǔ)早期暴露 Bean 的地方。當(dāng)一個(gè) Bean 剛剛被實(shí)例化出來,但還沒有完成所有的初始化步驟時(shí),為了防止在循環(huán)依賴場景下出現(xiàn)問題,Spring 會(huì)將這個(gè)早期暴露的 Bean 先放入二級(jí)緩存中。這樣,當(dāng)其他 Bean 需要依賴它時(shí),可以先從二級(jí)緩存中獲取到這個(gè)尚未完全初始化的 Bean 引用,避免了不必要的遞歸創(chuàng)建。例如,在前面提到的 PropertyBeanA 和 PropertyBeanB 的循環(huán)依賴場景中,當(dāng)創(chuàng)建 PropertyBeanA 并將其早期暴露到二級(jí)緩存后,在創(chuàng)建 PropertyBeanB 時(shí)就可以從二級(jí)緩存中獲取到 PropertyBeanA 的引用,從而打破循環(huán)。

二級(jí)緩存的使用時(shí)機(jī)

在 Bean 實(shí)例化完成后,Spring 會(huì)將該 Bean 的早期引用放入二級(jí)緩存中。在后續(xù)的屬性注入和初始化過程中,如果發(fā)現(xiàn)有其他 Bean 依賴于該 Bean,就會(huì)先從二級(jí)緩存中嘗試獲取該 Bean 的引用。當(dāng) Bean 最終完成所有初始化操作并放入一級(jí)緩存后,會(huì)從二級(jí)緩存中移除該 Bean 的引用。

三級(jí)緩存-延遲創(chuàng)建Bean的工廠

三級(jí)緩存的定義與數(shù)據(jù)結(jié)構(gòu)

三級(jí)緩存是一個(gè) Map<String, ObjectFactory<?>> 結(jié)構(gòu),其中鍵是 Bean 的名稱,值是一個(gè) ObjectFactory 對(duì)象。ObjectFactory 是一個(gè)函數(shù)式接口,它只有一個(gè) getObject() 方法,用于創(chuàng)建 Bean 對(duì)象。

// 三級(jí)緩存的示例定義
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

三級(jí)緩存的作用

三級(jí)緩存的主要作用是提供一種延遲加載和創(chuàng)建 Bean 的機(jī)制。在 Bean 創(chuàng)建過程中,尤其是在處理循環(huán)依賴時(shí),它能夠通過 ObjectFactory 來獲取早期暴露的 Bean 對(duì)象。通過這種方式,Spring 可以在合適的時(shí)機(jī)創(chuàng)建 Bean,避免了因?yàn)橹苯觿?chuàng)建而可能導(dǎo)致的循環(huán)依賴問題。例如,在某些情況下,可能需要對(duì) Bean 進(jìn)行一些額外的處理或增強(qiáng),通過 ObjectFactory 可以在需要的時(shí)候才進(jìn)行這些操作,而不是在 Bean 實(shí)例化時(shí)就進(jìn)行。

三級(jí)緩存的使用時(shí)機(jī)

在 Bean 實(shí)例化完成后,Spring 會(huì)創(chuàng)建一個(gè) ObjectFactory 對(duì)象,并將其放入三級(jí)緩存中。這個(gè) ObjectFactory 對(duì)象會(huì)在需要獲取早期暴露的 Bean 時(shí)被調(diào)用。當(dāng)從三級(jí)緩存中獲取到 ObjectFactory 并調(diào)用其 getObject() 方法后,會(huì)將獲取到的 Bean 放入二級(jí)緩存中,并從三級(jí)緩存中移除該 ObjectFactory。

利用三級(jí)緩存解決循環(huán)依賴的詳細(xì)過程

創(chuàng)建Bean A

實(shí)例化Bean A

當(dāng) Spring 容器開始創(chuàng)建 BeanA 時(shí),首先會(huì)調(diào)用 BeanA 的構(gòu)造函數(shù)來實(shí)例化它。在這個(gè)階段,BeanA 只是一個(gè)剛被創(chuàng)建出來的對(duì)象,它的所有屬性都還是默認(rèn)值,還沒有進(jìn)行屬性注入等操作。例如,如果 BeanA 有一個(gè) name 屬性,此時(shí) name 的值為 null。

// BeanA 的構(gòu)造函數(shù)示例
public class BeanA {
    private BeanB beanB;
    public BeanA() {
        // 構(gòu)造函數(shù)邏輯
    }
}

將Bean A的早期引用放入三級(jí)緩存

在 BeanA 實(shí)例化完成后,Spring 會(huì)創(chuàng)建一個(gè) ObjectFactory 對(duì)象,該對(duì)象封裝了獲取 BeanA 早期引用的邏輯。然后將這個(gè) ObjectFactory 對(duì)象放入三級(jí)緩存中。

// 將 BeanA 的早期引用放入三級(jí)緩存的示例邏輯
singletonFactories.put("beanA", () -> getEarlyBeanReference(beanName, mbd, bean));

屬性注入時(shí)發(fā)現(xiàn)對(duì)Bean B的依賴

開始屬性注入

在 BeanA 實(shí)例化完成并將其早期引用放入三級(jí)緩存后,Spring 開始進(jìn)行 BeanA 的屬性注入。在這個(gè)過程中,會(huì)檢查 BeanA 的所有依賴項(xiàng)。

發(fā)現(xiàn)對(duì)Bean B的依賴

當(dāng)檢查到 BeanA 依賴于 BeanB 時(shí),Spring 容器會(huì)嘗試去獲取 BeanB。由于此時(shí)容器中還沒有完全初始化的 BeanB,所以 Spring 會(huì)進(jìn)入創(chuàng)建 BeanB 的流程。

創(chuàng)建Bean B及發(fā)現(xiàn)對(duì)Bean A的依賴

實(shí)例化Bean B

Spring 容器開始創(chuàng)建 BeanB,同樣會(huì)調(diào)用 BeanB 的構(gòu)造函數(shù)來實(shí)例化它。此時(shí),BeanB 也只是一個(gè)剛被創(chuàng)建出來的對(duì)象,其屬性還未進(jìn)行注入。

// BeanB 的構(gòu)造函數(shù)示例
public class BeanB {
    private BeanA beanA;
    public BeanB() {
        // 構(gòu)造函數(shù)邏輯
    }
}

將Bean B的早期引用放入三級(jí)緩存

與 BeanA 類似,在 BeanB 實(shí)例化完成后,Spring 會(huì)創(chuàng)建一個(gè) ObjectFactory 對(duì)象,并將其放入三級(jí)緩存中。

發(fā)現(xiàn)對(duì)Bean A的依賴

在進(jìn)行 BeanB 的屬性注入時(shí),發(fā)現(xiàn) BeanB 依賴于 BeanA。此時(shí),Spring 會(huì)嘗試從緩存中獲取 BeanA。

從三級(jí)緩存獲取Bean A

檢查緩存

Spring 首先會(huì)檢查一級(jí)緩存中是否存在 BeanA,由于 BeanA 還未完全初始化,所以一級(jí)緩存中不存在。然后會(huì)檢查二級(jí)緩存,二級(jí)緩存中也沒有。最后會(huì)檢查三級(jí)緩存,發(fā)現(xiàn)三級(jí)緩存中有 BeanA 對(duì)應(yīng)的 ObjectFactory。

獲取早期暴露的Bean A

Spring 會(huì)調(diào)用 BeanA 對(duì)應(yīng)的 ObjectFactory 的 getObject() 方法,獲取到早期暴露的 BeanA 的引用。然后將這個(gè) BeanA 的引用從三級(jí)緩存中移除,并放入二級(jí)緩存中。

完成Bean B的創(chuàng)建

注入Bean A的引用

將從二級(jí)緩存中獲取到的 BeanA 的引用注入到 BeanB 中。此時(shí),BeanB 雖然獲取到的是一個(gè)尚未完全初始化的 BeanA 引用,但這已經(jīng)足夠打破循環(huán)依賴。

完成Bean B的初始化

BeanB 繼續(xù)完成其他屬性的注入和初始化操作。在這個(gè)過程中,由于已經(jīng)有了 BeanA 的引用,所以不會(huì)再觸發(fā)對(duì) BeanA 的重新創(chuàng)建,從而避免了循環(huán)依賴。最終,BeanB 完成所有的初始化操作,被放入一級(jí)緩存中。

完成Bean A的創(chuàng)建

獲取Bean B的引用

BeanA 獲取到已經(jīng)完全創(chuàng)建好的 BeanB,這個(gè) BeanB 是從一級(jí)緩存中獲取的。

完成Bean A的初始化

BeanA 繼續(xù)完成自身剩余的屬性注入和其他初始化操作。最后,BeanA 也完成了所有的初始化步驟,被放入一級(jí)緩存中。此時(shí),BeanA 和 BeanB 都已經(jīng)成功創(chuàng)建,并且它們之間的循環(huán)依賴問題也得到了妥善解決。

通過這樣精細(xì)的三級(jí)緩存機(jī)制,Spring 成功地打破了循環(huán)依賴的僵局,確保了 Bean 之間能夠正確地進(jìn)行依賴注入和初始化,為開發(fā)者提供了一個(gè)穩(wěn)定、可靠的依賴管理環(huán)境。同時(shí),我們也應(yīng)該注意到,這種機(jī)制對(duì)于構(gòu)造器注入的循環(huán)依賴是無法解決的,在開發(fā)過程中應(yīng)盡量避免構(gòu)造器注入的循環(huán)依賴情況的發(fā)生。

以上就是Spring用三級(jí)緩存處理循環(huán)依賴的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring三級(jí)緩存處理循環(huán)依賴的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中的重寫(Override)與重載(Overload)詳解

    Java中的重寫(Override)與重載(Overload)詳解

    這篇文章主要介紹了Java中的重寫(Override)與重載(Overload)使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • RocketMQ的順序消費(fèi)機(jī)制詳解

    RocketMQ的順序消費(fèi)機(jī)制詳解

    這篇文章主要介紹了RocketMQ的順序消費(fèi)機(jī)制詳解,順序消息是指對(duì)于一個(gè)指定的?Topic?,消息嚴(yán)格按照先進(jìn)先出(FIFO)的原則進(jìn)行消息發(fā)布和消費(fèi),即先發(fā)布的消息先消費(fèi),后發(fā)布的消息后消費(fèi),,需要的朋友可以參考下
    2023-10-10
  • 深入探討Java內(nèi)存區(qū)域

    深入探討Java內(nèi)存區(qū)域

    本篇文章對(duì)Java內(nèi)存區(qū)域的使用進(jìn)行了詳細(xì)的介紹,內(nèi)容很全面,需要的朋友可以參考下
    2015-07-07
  • Java如何計(jì)算兩個(gè)時(shí)間段內(nèi)的工作日天數(shù)

    Java如何計(jì)算兩個(gè)時(shí)間段內(nèi)的工作日天數(shù)

    這篇文章主要介紹了Java如何計(jì)算兩個(gè)時(shí)間段內(nèi)的工作日天數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • spring boot 實(shí)現(xiàn)熱部署的幾種方式及配置方法

    spring boot 實(shí)現(xiàn)熱部署的幾種方式及配置方法

    這篇文章主要介紹了spring boot 實(shí)現(xiàn)熱部署的幾種方式及配置方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2025-05-05
  • SpringBoot啟動(dòng)流程入口參數(shù)創(chuàng)建對(duì)象源碼分析

    SpringBoot啟動(dòng)流程入口參數(shù)創(chuàng)建對(duì)象源碼分析

    這篇文章主要為大家介紹了SpringBoot啟動(dòng)流程入口參數(shù)研究及創(chuàng)建對(duì)象源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • 如何替換@PathVariable中的變量

    如何替換@PathVariable中的變量

    這篇文章主要介紹了如何替換@PathVariable中的變量,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • logback之如何按日期和大小切分日志

    logback之如何按日期和大小切分日志

    這篇文章主要介紹了logback之如何按日期和大小切分日志問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • JAVA JNI原理詳細(xì)介紹及簡單實(shí)例代碼

    JAVA JNI原理詳細(xì)介紹及簡單實(shí)例代碼

    這篇文章主要介紹了JAVA JNI原理的相關(guān)資料,這里提供簡單實(shí)例代碼,需要的朋友可以參考下
    2016-12-12
  • Springboot?JPA如何使用distinct返回對(duì)象

    Springboot?JPA如何使用distinct返回對(duì)象

    這篇文章主要介紹了Springboot?JPA如何使用distinct返回對(duì)象,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評(píng)論

保定市| 青神县| 怀化市| 佳木斯市| 钦州市| 辰溪县| 辉南县| 广宁县| 开江县| 中方县| 凤凰县| 宣城市| 朝阳区| 札达县| 和静县| 新蔡县| 龙南县| 神池县| 永宁县| 武汉市| 陆川县| 英吉沙县| 鄂州市| 义马市| 西宁市| 广德县| 益阳市| 自贡市| 红河县| 滦南县| 德江县| 黔东| 朝阳区| 新郑市| 开远市| 贡山| 汾阳市| 苗栗县| 敦化市| 玉环县| 大丰市|