Spring中的循環(huán)依賴問(wèn)題
在 Spring 中,循環(huán)依賴是指兩個(gè)或多個(gè) Bean 相互依賴,導(dǎo)致在創(chuàng)建過(guò)程中出現(xiàn)了依賴死鎖的問(wèn)題。
為了解決循環(huán)依賴,Spring 引入了三級(jí)緩存機(jī)制。
了解為什么需要三級(jí)緩存機(jī)制,首先要明白循環(huán)依賴是如何發(fā)生的,以及兩級(jí)緩存為什么不足夠。
一、循環(huán)依賴是什么?
假設(shè)有兩個(gè) Bean A
和 B
:
A
依賴于B
B
依賴于A
如果沒(méi)有緩存機(jī)制,Spring 在創(chuàng)建 A
時(shí)會(huì)發(fā)現(xiàn)它需要 B
,于是去創(chuàng)建 B
,但在創(chuàng)建 B
時(shí)又發(fā)現(xiàn)需要 A
,這時(shí)就會(huì)產(chǎn)生循環(huán)依賴,最終導(dǎo)致棧溢出或拋出異常。
二、三級(jí)緩存機(jī)制
Spring 使用三級(jí)緩存(三級(jí)依賴處理機(jī)制)來(lái)解決循環(huán)依賴問(wèn)題,分別是:
一級(jí)緩存:singletonObjects
- 用于存儲(chǔ)完全初始化好的單例對(duì)象。
- 完全初始化后的 Bean 會(huì)放入一級(jí)緩存中,表示該 Bean 已經(jīng)準(zhǔn)備好可以使用了。
二級(jí)緩存:earlySingletonObjects
- 用于存儲(chǔ)提前暴露的 Bean,主要是尚未完成依賴注入但已經(jīng)實(shí)例化的 Bean。
- 這個(gè)緩存用于提前暴露尚未完成初始化的 Bean,防止循環(huán)依賴無(wú)法解決。通常情況下,如果某個(gè) Bean 已經(jīng)實(shí)例化但還沒(méi)有完成后續(xù)的屬性填充等操作,它會(huì)存放在這個(gè)緩存中。
三級(jí)緩存:singletonFactories
- 用于存儲(chǔ) Bean 的 ObjectFactory(對(duì)象工廠),這個(gè)工廠提供對(duì)該 Bean 的代理對(duì)象(比如 AOP 代理對(duì)象)的創(chuàng)建邏輯。
- 三級(jí)緩存的存在使得 Spring 可以在 Bean 創(chuàng)建的早期階段將未完全初始化的 Bean 提供出來(lái),尤其是代理對(duì)象。這樣即使 Bean 還沒(méi)有完成依賴注入,也能通過(guò)工廠獲得它的一個(gè)早期引用。
三、為什么需要三級(jí)緩存??jī)杉?jí)不行嗎?
假如只使用一級(jí)緩存和二級(jí)緩存:
- 一級(jí)緩存只存儲(chǔ)已經(jīng)完全初始化好的 Bean,顯然無(wú)法解決循環(huán)依賴問(wèn)題,因?yàn)?Bean 尚未完成初始化時(shí)無(wú)法放入一級(jí)緩存。
- 二級(jí)緩存則存儲(chǔ)提前暴露的 Bean,但這通常是直接的原始對(duì)象,而不是代理對(duì)象。如果應(yīng)用了 AOP 或者需要?jiǎng)?chuàng)建代理對(duì)象的場(chǎng)景中,依賴的 Bean 如果在循環(huán)依賴中被提前暴露時(shí),可能無(wú)法應(yīng)用正確的代理。
而 三級(jí)緩存 允許通過(guò) ObjectFactory
這種延遲加載的方式,在需要的時(shí)候創(chuàng)建早期引用,包括創(chuàng)建代理對(duì)象。這確保了即使在循環(huán)依賴中,Spring 也可以在合適的時(shí)間點(diǎn)創(chuàng)建完整的代理對(duì)象,而不是僅僅提供原始對(duì)象。
總結(jié)
三級(jí)緩存機(jī)制的關(guān)鍵點(diǎn)在于:
- 二級(jí)緩存不能解決 Bean 代理的問(wèn)題,特別是在涉及到 AOP 的情況下。
- 三級(jí)緩存通過(guò)引入
ObjectFactory
,可以確保在代理場(chǎng)景下也能處理循環(huán)依賴,提前暴露還未完全初始化的代理對(duì)象。
因此,兩級(jí)緩存不足以解決所有循環(huán)依賴問(wèn)題,特別是在涉及到代理對(duì)象的情況下,三級(jí)緩存的機(jī)制顯得非常必要。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
淺談springmvc的DispatcherServlet分析
本篇文章主要介紹了淺談springmvc的DispatcherServlet分析,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09詳解在spring boot中配置多個(gè)DispatcherServlet
本篇文章主要介紹了詳解在spring boot中配置多個(gè)DispatcherServlet,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-03-03IntelliJ IDEA全局內(nèi)容搜索和替換教程圖解
很多朋友在做項(xiàng)目時(shí),會(huì)在整個(gè)項(xiàng)目里活指定文件夾下進(jìn)行全局搜索和替換,下面小編給大家?guī)?lái)了IntelliJ IDEA全局內(nèi)容搜索和替換教程圖解,需要的朋友參考下吧2018-04-04java8 stream 由一個(gè)list轉(zhuǎn)化成另一個(gè)list案例
這篇文章主要介紹了java8 stream 由一個(gè)list轉(zhuǎn)化成另一個(gè)list案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08java 如何實(shí)現(xiàn)多語(yǔ)言配置i18n
這篇文章主要介紹了java 如何實(shí)現(xiàn)多語(yǔ)言配置i18n的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08基于Spring實(shí)現(xiàn)自定義錯(cuò)誤信息返回詳解
這篇文章主要為大家詳細(xì)介紹了如何基于Spring實(shí)現(xiàn)自定義錯(cuò)誤信息返回效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03java實(shí)現(xiàn)自定義時(shí)鐘并實(shí)現(xiàn)走時(shí)功能
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)自定義時(shí)鐘并實(shí)現(xiàn)走時(shí)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06SpringBoot實(shí)現(xiàn)elasticsearch索引操作的代碼示例
這篇文章主要給大家介紹了SpringBoot如何實(shí)現(xiàn)elasticsearch 索引操作,文中有詳細(xì)的代碼示例,感興趣的同學(xué)可以參考閱讀下2023-07-07