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

Spring 中的循環(huán)引用問題解決方法

 更新時(shí)間:2025年04月27日 11:11:21   作者:我可是ikun啊  
這篇文章主要介紹了Spring 中的循環(huán)引用問題解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

本章來聊聊Spring 中的循環(huán)引用問題該如何解決。這里聊的很粗糙,并沒有那么細(xì)節(jié),只是大概了解了一點(diǎn)。

什么是循環(huán)引用?

如下圖所示:

圖中有兩個(gè)類,一個(gè) Class A ,A 中又引用了 B,Class B 中又引用了 A 。(基于 setter 注入和字段注入)

當(dāng) Spring 容器想要?jiǎng)?chuàng)建 A 時(shí)發(fā)現(xiàn) A 中需要使用到 B 對(duì)象,此時(shí)就去創(chuàng)建 B ,在創(chuàng)建 B 對(duì)象時(shí)又發(fā)現(xiàn)還需要引用 A ,此時(shí)就發(fā)生循環(huán)引用了。

當(dāng)然循環(huán)引用并非就是 A 引用 B ,B 引用 A 的情況,還有很多,如:

當(dāng)然,此圖比較草率。

有了循環(huán)依賴就會(huì)有死循環(huán)的問題。

循環(huán)依賴

來看看死循環(huán)產(chǎn)生的過程:

為什么這里是半成品呢?要是熟悉 Spring-bean 的 生命周期就會(huì)知道,首先會(huì)去調(diào)用的是構(gòu)造函數(shù),像一些依賴注入啊,還有一些接口的實(shí)現(xiàn)的方法重寫啊還有后置處理器初始化方法,想這些都還沒有去執(zhí)行,所以說他還是一個(gè)半成品對(duì)象。

繼續(xù)向后執(zhí)行:

此時(shí),在容器中找不到 A 對(duì)象啊,那么他又去實(shí)例化 A 了。此時(shí)死循環(huán)就產(chǎn)生了,這個(gè)就叫做 循環(huán)依賴。

三級(jí)緩存:

緩存級(jí)別源碼名稱作用
一級(jí)緩存singletonObjects存儲(chǔ)已經(jīng)完全初始化好的單例 Bean。當(dāng) Bean 初始化完成,所有依賴項(xiàng)都已注入,就會(huì)放入此緩存,供后續(xù)獲取 Bean 時(shí)直接使用,提高獲取單例 Bean 的效率。
簡單來說:單例池,緩存已經(jīng)經(jīng)歷了完整的生命周期,已經(jīng)初始化完成了的對(duì)象。
二級(jí)緩存
earlySingletonObjects

存儲(chǔ)提前暴露的原始 Bean,即已經(jīng)開始實(shí)例化但還未完成初始化(如未填充屬性)的 Bean。用于解決在構(gòu)造器注入過程中發(fā)生的循環(huán)依賴問題,確保在循環(huán)依賴情況下 Bean 只被創(chuàng)建一次,也能解決多線程并發(fā)下獲取不完整 Bean 的性能問題。

簡單來說:緩存早期的 bean 對(duì)象(生命周期未完成)

三級(jí)緩存singletonFactories

存儲(chǔ) Bean 的 ObjectFactory,用于生成原始 Bean 的代理對(duì)象。當(dāng)遇到循環(huán)依賴且涉及到代理對(duì)象創(chuàng)建時(shí),Spring 會(huì)將 ObjectFactory 放入三級(jí)緩存,在后續(xù)需要時(shí)通過它來獲取 Bean 的實(shí)例,以處理代理相關(guān)情況,統(tǒng)一處理普通 Bean 和代理 Bean。

簡單來說:緩存 objectFactory,表示對(duì)象工廠,用來創(chuàng)建某個(gè)對(duì)象

我們一個(gè)個(gè)來說

三級(jí)緩存解決循環(huán)依賴

回到上個(gè)圖片:

按照一級(jí)緩存的邏輯,走完一個(gè)生命周期才能將對(duì)象存儲(chǔ)在緩存中?。ㄈ前氤善罚敲催@個(gè)流程中必然是沒有對(duì)象存在緩存中的。

走到這里發(fā)現(xiàn)了,單憑一級(jí)緩存是沒有辦法解決循環(huán)依賴的。

要想打破這個(gè)循環(huán)依賴,需要一個(gè)中間人的參與(暫時(shí)存放一個(gè)半成品的 對(duì)象),這個(gè)中間人就是二級(jí)緩存。

二級(jí)緩存

我們重新走一遍流程:

實(shí)例化一個(gè) A 對(duì)象,此時(shí)生成一個(gè) 原始對(duì)象 A(半成品的 A )只是開辟了空間,執(zhí)行了一個(gè)構(gòu)造方法
就將這個(gè)方法存入這個(gè)二級(jí)緩存中
發(fā)現(xiàn)創(chuàng)建 A 對(duì)象需要依賴 B 對(duì)象,就注入 B,B 又不存在,那么就實(shí)例化一個(gè) B
B還是和之前的 A 一樣,同時(shí)也將 原始對(duì)象存入這個(gè) 二級(jí)緩存中
但是在 B 中注入 A 時(shí),發(fā)現(xiàn)了緩存中有一個(gè)原始對(duì)象 A ,那么就可以打破循環(huán)依賴,使 B創(chuàng)建成功 
此時(shí) B 已經(jīng)創(chuàng)建成功了,就可以存儲(chǔ)到 一級(jí)緩存中了,那么也就可以將 B 注入進(jìn) A 了
A 也就可以創(chuàng)建成功,成功之后也需要存入一級(jí)緩存(此時(shí)二級(jí)緩存中的半成品對(duì)象也可以清理掉了)

此時(shí)似乎使用 二級(jí)緩存 + 一級(jí)緩存 就可以解決循環(huán)依賴的問題了啊,那還要三級(jí)緩存干嘛呢?

我們假設(shè),如果這個(gè) A 是個(gè)代理對(duì)象呢?

兩個(gè)原因:

代理對(duì)象創(chuàng)建時(shí)機(jī)的問題:Spring 的代理對(duì)象通常是在 Bean 初始化過程的特定階段創(chuàng)建的(例如在初始化方法執(zhí)行之后)。在處理循環(huán)依賴時(shí),如果僅使用一級(jí)和二級(jí)緩存,當(dāng)一個(gè) Bean 在創(chuàng)建過程中需要依賴另一個(gè) Bean,而另一個(gè) Bean 又依賴這個(gè) Bean 時(shí),由于代理對(duì)象還未創(chuàng)建,二級(jí)緩存中存儲(chǔ)的只是原始的 Bean 實(shí)例,不是代理對(duì)象。如果將原始 Bean 實(shí)例注入到依賴它的 Bean 中,就會(huì)導(dǎo)致依賴方持有的是原始 Bean 而不是代理 Bean,這不符合 Spring 的設(shè)計(jì)要求。
保證代理對(duì)象的一致性:對(duì)于同一個(gè) Bean,無論從何處獲取,都應(yīng)該保證是同一個(gè)代理對(duì)象。如果沒有三級(jí)緩存,在循環(huán)依賴場景下可能會(huì)出現(xiàn)多次創(chuàng)建代理對(duì)象的情況,從而破壞了單例 Bean 的唯一性和代理對(duì)象的一致性。

三級(jí)緩存

此時(shí)我們使用 三級(jí)緩存來代替二級(jí)緩存

一級(jí)緩存+二級(jí)緩存 不是不能解決 代理對(duì)象的問題嗎,那么這里就使用三級(jí)緩存來做,再注入 A 時(shí),通過對(duì)象工廠生成,你是代理對(duì)象那就生成一個(gè)代理對(duì)象再注入,你是一個(gè)普通對(duì)象就生成一個(gè)普通對(duì)象 再注入。

雖然說 通過 A 的 ObjectFactory 對(duì)象創(chuàng)建了一個(gè)代理對(duì)象,但是此時(shí)還是一個(gè)半成品對(duì)象,這里就需要 二級(jí)緩存 來存放了。此時(shí)存放的是一個(gè) A 的代理對(duì)象(半成品)。在完成的對(duì)象存放在一級(jí)緩存中后被刪除了。

到這里借助了三級(jí)緩存,解決了大部分循環(huán)依賴的問題;借助工廠類,幫助我們生成工廠對(duì)象來產(chǎn)生代理對(duì)象。

為什么是大部分的循環(huán)依賴呢,因?yàn)槟承┭h(huán)引用 spring 框架解決不了,需要手動(dòng)來解決。例如:構(gòu)造方法注入產(chǎn)生的循環(huán)依賴。

class A {
    private B b;
    public A(B b) {
        this.b = b;
    }
}
class B {
    private A a;
    public B(A a) {
        this.a = a;
    }
}

三級(jí)緩存可以解決初始化過程中的循環(huán)依賴,卻無法解決構(gòu)造函數(shù)產(chǎn)生的循環(huán)依賴,那么該怎么辦解決呢?

我們只需要在 參數(shù)之前加上一個(gè) @Lazy (延遲加載)

public A(@Lazy B b) {
        System.out.println("A的構(gòu)造方法");
        this.b = b;
    }

延遲加載的意思就是什么時(shí)候需要對(duì)象再進(jìn)行對(duì)象的創(chuàng)建,而不再是直接把對(duì)象注入進(jìn)來。

到此就解決循環(huán)依賴了。

三級(jí)緩存 + 一級(jí)緩存 存在的問題

再回顧三級(jí)緩存中,既然三級(jí)緩存代替了二級(jí)緩存,那么能否使用通過 一級(jí)緩存 + 三級(jí)緩存來解決循環(huán)依賴呢?

沒有二級(jí)緩存的后果:

如果沒有二級(jí)緩存,當(dāng)出現(xiàn)循環(huán)依賴時(shí),雖然三級(jí)緩存可以提供早期引用,但無法區(qū)分同一個(gè) Bean 的不同狀態(tài)。例如,無法區(qū)分是正在創(chuàng)建的 Bean 還是已經(jīng)創(chuàng)建好但還未注入屬性的 Bean,可能會(huì)導(dǎo)致錯(cuò)誤的 Bean 引用被使用,進(jìn)而引發(fā)循環(huán)依賴問題無法正確解決。

每次需要使用工廠生成好的對(duì)象直接去二級(jí)緩存拿出來就可以了,不用再次去生成這個(gè)對(duì)象。這也是二級(jí)緩存的核心作用之一。

如果沒有二級(jí)緩存,那么就有可能產(chǎn)生多例的情況,此時(shí)處理起來就更麻煩了。

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

相關(guān)文章

最新評(píng)論

商洛市| 明星| 曲沃县| 黄浦区| 大宁县| 固阳县| 郑州市| 兖州市| 广州市| 兖州市| 牙克石市| 焦作市| 临湘市| 三门县| 循化| 永顺县| 翁牛特旗| 仲巴县| 资溪县| 吉木乃县| 扶绥县| 武安市| 兰坪| 井陉县| 余江县| 洪雅县| 嘉兴市| 钟山县| 平安县| 淳化县| 浪卡子县| 凤城市| 广南县| 赣榆县| 瓦房店市| 赣榆县| 册亨县| 长顺县| 尚义县| 舒兰市| 红安县|