欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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 的三級緩存機制解決,但構(gòu)造器注入的循環(huán)依賴無法通過該機制解決,這是因為構(gòu)造器注入要求在 Bean 實例化時就完成依賴注入,而此時 Bean 還未進入可以暴露的階段。

循環(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,如果存在則直接使用。

一級緩存的使用時機

在 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)完成了實例化,但還沒有進行屬性填充等后續(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)。

二級緩存的使用時機

在 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 的機制。在 Bean 創(chuàng)建過程中,尤其是在處理循環(huán)依賴時,它能夠通過 ObjectFactory 來獲取早期暴露的 Bean 對象。通過這種方式,Spring 可以在合適的時機創(chuàng)建 Bean,避免了因為直接創(chuàng)建而可能導(dǎo)致的循環(huán)依賴問題。例如,在某些情況下,可能需要對 Bean 進行一些額外的處理或增強,通過 ObjectFactory 可以在需要的時候才進行這些操作,而不是在 Bean 實例化時就進行。

三級緩存的使用時機

在 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)值,還沒有進行屬性注入等操作。例如,如果 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 開始進行 BeanA 的屬性注入。在這個過程中,會檢查 BeanA 的所有依賴項。

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

當(dāng)檢查到 BeanA 依賴于 BeanB 時,Spring 容器會嘗試去獲取 BeanB。由于此時容器中還沒有完全初始化的 BeanB,所以 Spring 會進入創(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)建出來的對象,其屬性還未進行注入。

// 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的依賴

在進行 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ì)的三級緩存機制,Spring 成功地打破了循環(huán)依賴的僵局,確保了 Bean 之間能夠正確地進行依賴注入和初始化,為開發(fā)者提供了一個穩(wěn)定、可靠的依賴管理環(huán)境。同時,我們也應(yīng)該注意到,這種機制對于構(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)文章

  • SpringBoot啟動之SpringApplication初始化詳解

    SpringBoot啟動之SpringApplication初始化詳解

    這篇文章主要介紹了SpringBoot啟動之SpringApplication初始化詳解,首先初始化資源加載器,默認(rèn)為null;斷言判斷主要資源類不能為null,否則報錯,需要的朋友可以參考下
    2024-01-01
  • Spring Boot 簡單使用EhCache緩存框架的方法

    Spring Boot 簡單使用EhCache緩存框架的方法

    本篇文章主要介紹了Spring Boot 簡單使用EhCache緩存框架的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • Java對數(shù)器驗證算法詳解

    Java對數(shù)器驗證算法詳解

    這篇文章主要介紹了Java對數(shù)器驗證算法,Java對數(shù)函數(shù)的計算方法非常有問題,然而在API中卻有驚人的誤差。但是假如運用了以下的方法,用Java處理數(shù)字所碰到的小麻煩就可以輕而易舉的解決了
    2023-04-04
  • Java8 Instant 時間戳實例講解

    Java8 Instant 時間戳實例講解

    Instant類是Java8 中補充的一個 時間戳類,nstant 可以使用靜態(tài)方法 now()或者of()方法來創(chuàng)建一個實例對象,本文通過實例代碼講解Java8 Instant 時間戳,感興趣的朋友跟隨小編一起看看吧
    2022-11-11
  • 詳解JDK自帶javap命令反編譯class文件和Jad反編譯class文件(推薦使用jad)

    詳解JDK自帶javap命令反編譯class文件和Jad反編譯class文件(推薦使用jad)

    這篇文章主要介紹了JDK自帶javap命令反編譯class文件和Jad反編譯class文件(推薦使用jad),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-09-09
  • java并發(fā)編程工具類JUC之ArrayBlockingQueue

    java并發(fā)編程工具類JUC之ArrayBlockingQueue

    類ArrayBlockingQueue是BlockingQueue接口的實現(xiàn)類,它是有界的阻塞隊列,內(nèi)部使用數(shù)組存儲隊列元素,通過代碼給大家說明如何初始化一個ArrayBlockingQueue,并向其中添加一個對象,對java并發(fā)編程工具類ArrayBlockingQueue相關(guān)知識感興趣的朋友一起看看吧
    2021-05-05
  • 淺談DetachedCriteria和Criteria的使用方法(必看)

    淺談DetachedCriteria和Criteria的使用方法(必看)

    下面小編就為大家?guī)硪黄獪\談DetachedCriteria和Criteria的使用方法(必看)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • mybatis數(shù)組和集合的長度判斷及插入方式

    mybatis數(shù)組和集合的長度判斷及插入方式

    這篇文章主要介紹了mybatis數(shù)組和集合的長度判斷及插入方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java基于NIO實現(xiàn)聊天室功能

    Java基于NIO實現(xiàn)聊天室功能

    這篇文章主要為大家詳細(xì)介紹了Java基于NIO實現(xiàn)聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • java + dom4j.jar提取xml文檔內(nèi)容

    java + dom4j.jar提取xml文檔內(nèi)容

    這篇文章主要為大家詳細(xì)介紹了java + dom4j.jar提取xml文檔內(nèi)容,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08

最新評論