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

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

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

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

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

在 Spring 框架中,依賴注入是其核心特性之一,它允許對象之間的依賴關(guān)系在運行時動態(tài)注入。然而,當(dāng)多個 Bean 之間的依賴關(guān)系形成一個閉環(huán)時,就會出現(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)建這兩個 Bean 時,會陷入無限循環(huá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ī)制解決,但構(gòu)造器注入的循環(huán)依賴無法通過該機(jī)制解決,這是因為構(gòu)造器注入要求在 Bean 實例化時就完成依賴注入,而此時 Bean 還未進(jìn)入可以暴露的階段。

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

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

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

一級緩存-完全初始化Bean的倉庫

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

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

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

一級緩存的作用

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

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

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

二級緩存-早期暴露的Bean臨時存儲

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

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

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

二級緩存的作用

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

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

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

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

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

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

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

三級緩存的作用

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

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

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

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

創(chuàng)建Bean A

實例化Bean A

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

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

將Bean A的早期引用放入三級緩存

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

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

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

開始屬性注入

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

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

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

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

實例化Bean B

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

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

將Bean B的早期引用放入三級緩存

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

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

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

從三級緩存獲取Bean A

檢查緩存

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

獲取早期暴露的Bean A

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

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

注入Bean A的引用

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

完成Bean B的初始化

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

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

獲取Bean B的引用

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

完成Bean A的初始化

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

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

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

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

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

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

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

    如何替換@PathVariable中的變量

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

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

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

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

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

    Springboot?JPA如何使用distinct返回對象

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

最新評論

温宿县| 临城县| 金堂县| 临潭县| 泰宁县| 台中县| 杭锦旗| 娄底市| 怀安县| 体育| 沅江市| 合江县| 定远县| 梅河口市| 勐海县| 错那县| 靖西县| 昌平区| 北海市| 莲花县| 阿拉善右旗| 台南县| 武夷山市| 韩城市| 曲阜市| 沈阳市| 永兴县| 西吉县| 闽侯县| 韶关市| 多伦县| 九台市| 临沭县| 西吉县| 龙川县| 治县。| 临清市| 龙泉市| 寿阳县| 靖远县| 博白县|