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

一文教你如何通過(guò)三級(jí)緩存解決Spring循環(huán)依賴

 更新時(shí)間:2023年07月20日 09:31:53   作者:Java知識(shí)庫(kù)  
這篇文章主要介紹了如何通過(guò)三級(jí)緩存解決?Spring?循環(huán)依賴,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考價(jià)值,需要的朋友可以參考下

以下內(nèi)容基于 Spring6.0.4。

1. 循環(huán)依賴

1.1 什么是循環(huán)依賴

首先,什么是循環(huán)依賴?這個(gè)其實(shí)好理解,就是兩個(gè) Bean 互相依賴,類似下面這樣:

@Service
public class AService {
    @Autowired
    BService bService;
}
@Service
public class BService {
    @Autowired
    AService aService;
}

AService 和 BService 互相依賴:

這個(gè)應(yīng)該很好理解。

1.2 循環(huán)依賴的類型

一般來(lái)說(shuō),循環(huán)依賴有三種不同的形態(tài),上面 1.1 小節(jié)是其中一種。

另外兩種分別是三者依賴,如下圖:

這種循環(huán)依賴一般隱藏比較深,不易發(fā)覺(jué)。

還有自我依賴,如下圖:

一般來(lái)說(shuō),如果我們的代碼中出現(xiàn)了循環(huán)依賴,則說(shuō)明我們的代碼在設(shè)計(jì)的過(guò)程中可能存在問(wèn)題,我們應(yīng)該盡量避免循環(huán)依賴的發(fā)生。不過(guò)一旦發(fā)生了循環(huán)依賴,Spring 默認(rèn)也幫我們處理好了,當(dāng)然這并不能說(shuō)明循環(huán)依賴這種代碼就沒(méi)問(wèn)題。實(shí)際上在目前最新版的 Spring 中,循環(huán)依賴是要額外開(kāi)啟的,如果不額外配置,發(fā)生了循環(huán)依賴就直接報(bào)錯(cuò)了。
另外,Spring 并不能處理所有的循環(huán)依賴,后面松哥會(huì)和大家進(jìn)行分析。

2. 循環(huán)依賴解決思路

2.1 解決思路

那么對(duì)于循環(huán)依賴該如何解決呢?其實(shí)很簡(jiǎn)單,中加加入一個(gè)緩存就可以了,小伙伴們來(lái)看下面這張圖:

我們?cè)谶@里引入了一個(gè)緩存池。

當(dāng)我們需要?jiǎng)?chuàng)建 AService 的實(shí)例的時(shí)候,會(huì)首先通過(guò) Java 反射創(chuàng)建出來(lái)一個(gè)原始的 AService,這個(gè)原始 AService 可以簡(jiǎn)單理解為剛剛 new 出來(lái)(實(shí)際是剛剛通過(guò)反射創(chuàng)建出來(lái))還沒(méi)設(shè)置任何屬性的 AService,此時(shí),我們把這個(gè) AService 先存入到一個(gè)緩存池中。

接下來(lái)我們就需要給 AService 的屬性設(shè)置值了,同時(shí)還要處理 AService 的依賴,這時(shí)我們發(fā)現(xiàn) AService 依賴 BService,那么就去創(chuàng)建 BService 對(duì)象,結(jié)果創(chuàng)建 BService 的時(shí)候,發(fā)現(xiàn) BService 依賴 AService,那么此時(shí)就先從緩存池中取出來(lái) AService 先用著,然后繼續(xù) BService 創(chuàng)建的后續(xù)流程,直到 BService 創(chuàng)建完成后,將之賦值給 AService,此時(shí) AService 和 BService 就都創(chuàng)建完成了。

可能有小伙伴會(huì)說(shuō),BService 從緩存池中拿到的 AService 是一個(gè)半成品,并不是真正的最終的 AService,但是小伙伴們要知道,咱們 Java 是引用傳遞(也可以認(rèn)為是值傳遞,只不過(guò)這個(gè)值是內(nèi)存地址),BService 當(dāng)時(shí)拿到的是 AService 的引用,說(shuō)白了就是一塊內(nèi)存地址而已,根據(jù)這個(gè)地址找到的就是 AService,所以,后續(xù)如果 AService 創(chuàng)建完成后,BService 所拿到的 AService 就是完整的 AService 了。

那么上面提到的這個(gè)緩存池,在 Spring 容器中有一個(gè)專門的名字,就叫做 earlySingletonObjects,這是 Spring 三級(jí)緩存中的二級(jí)緩存,這里保存的是剛剛通過(guò)反射創(chuàng)建出來(lái)的 Bean,這些 Bean 還沒(méi)有經(jīng)歷過(guò)完整生命周期,Bean 的屬性可能都還沒(méi)有設(shè)置,Bean 需要的依賴都還沒(méi)有注入進(jìn)來(lái)。另外兩級(jí)緩存分別是:

  • singletonObjects:這是一級(jí)緩存,一級(jí)緩存中保存的是所有經(jīng)歷了完整生命周期的 Bean,即一個(gè) Bean 從創(chuàng)建、到屬性賦值、到各種處理器的執(zhí)行等等,都經(jīng)歷過(guò)了,就存到 singletonObjects 中,當(dāng)我們需要獲取一個(gè) Bean 的時(shí)候,首先會(huì)去一級(jí)緩存中查找,當(dāng)一級(jí)緩存中沒(méi)有的時(shí)候,才會(huì)考慮去二級(jí)緩存。
  • singletonFactories:這是三級(jí)緩存。在一級(jí)緩存和二級(jí)緩存中,緩存的 key 是 beanName,緩存的 value 則是一個(gè) Bean 對(duì)象,但是在三級(jí)緩存中,緩存的 value 是一個(gè) Lambda 表達(dá)式,通過(guò)這個(gè) Lambda 表達(dá)式可以創(chuàng)建出來(lái)目標(biāo)對(duì)象的一個(gè)代理對(duì)象。

有的小伙伴可能會(huì)覺(jué)得奇怪,按照上文的介紹,一級(jí)緩存和二級(jí)緩存就足以解決循環(huán)依賴了,為什么還冒出來(lái)一個(gè)三級(jí)緩存?那就得考慮 AOP 的情況了!

2.2 存在 AOP 怎么辦

上面給大家介紹的是普通的 Bean 創(chuàng)建,那確實(shí)沒(méi)有問(wèn)題。但是 Spring 中還有一個(gè)非常重要的能力,那就是 AOP。

說(shuō)到這里,我得先和小伙伴么說(shuō)一說(shuō) Spring 中 AOP 的創(chuàng)建流程。

正常來(lái)說(shuō)是我們首先通過(guò)反射獲取到一個(gè) Bean 的實(shí)例,然后就是給這個(gè) Bean 填充屬性,屬性填充完畢之后,接下來(lái)就是執(zhí)行各種 BeanPostProcessor 了,如果這個(gè) Bean 中有需要代理的方法,那么系統(tǒng)就會(huì)自動(dòng)配置對(duì)應(yīng)的后置處理器,松哥舉一個(gè)簡(jiǎn)單例子,假設(shè)我有如下一個(gè) Service:

@Service
public class UserService {
    @Async
    public void hello() {
        System.out.println("hello>>>"+Thread.currentThread().getName());
    }
}

那么系統(tǒng)就會(huì)自動(dòng)提供一個(gè)名為
AsyncAnnotationBeanPostProcessor 的處理器,在這個(gè)處理器中,系統(tǒng)會(huì)生成一個(gè)代理的 UserService 對(duì)象,并用這個(gè)對(duì)象代替原本的 UserService。

那么小伙伴們要搞清楚的是,原本的 UserService 和新生成的代理的 UserService 是兩個(gè)不同的對(duì)象,占兩塊不同的內(nèi)存地址?。?!

我們?cè)賮?lái)回顧下面這張圖:

如果 AService 最終是要生成一個(gè)代理對(duì)象的話,那么 AService 存到緩存池的其實(shí)還是原本的 AService,因?yàn)榇藭r(shí)還沒(méi)到處理 AOP 那一步(要先給各個(gè)屬性賦值,然后才是 AOP 處理),這就導(dǎo)致 BService 從緩存池里拿到的 AService 是原本的 AService,等到 BService 創(chuàng)建完畢之后,AService 的屬性賦值才完成,接下來(lái)在 AService 后續(xù)的創(chuàng)建流程中,AService 會(huì)變成了一個(gè)代理對(duì)象了,不是緩存池里的 AService 了,最終就導(dǎo)致 BService 所依賴的 AService 和最終創(chuàng)建出來(lái)的 AService 不是同一個(gè)。

為了解決這個(gè)問(wèn)題,Spring 引入了三級(jí)緩存 singletonFactories。

singletonFactories 的工作機(jī)制是這樣的(假設(shè) AService 最終是一個(gè)代理對(duì)象):

當(dāng)我們創(chuàng)建一個(gè) AService 的時(shí)候,通過(guò)反射剛把原始的 AService 創(chuàng)建出來(lái)之后,先去判斷當(dāng)前一級(jí)緩存中是否存在當(dāng)前 Bean,如果不存在,則:

  • 首先向三級(jí)緩存中添加一條記錄,記錄的 key 就是當(dāng)前 Bean 的 beanName,value 則是一個(gè) Lambda 表達(dá)式 ObjectFactory,通過(guò)執(zhí)行這個(gè) Lambda 可以給當(dāng)前 AService 生成代理對(duì)象。
  • 然后如果二級(jí)緩存中存在當(dāng)前 AService Bean,則移除掉。

現(xiàn)在繼續(xù)去給 AService 各個(gè)屬性賦值,結(jié)果發(fā)現(xiàn) AService 需要 BService,然后就去創(chuàng)建 BService,創(chuàng)建 BService 的時(shí)候,發(fā)現(xiàn) BService 又需要用到 AService,于是就先去一級(jí)緩存中查找是否有 AService,如果有,就使用,如果沒(méi)有,則去二級(jí)緩存中查找是否有 AService,如果有,就使用,如果沒(méi)有,則去三級(jí)緩存中找出來(lái)那個(gè) ObjectFactory,然后執(zhí)行這里的 getObject 方法,這個(gè)方法在執(zhí)行的過(guò)程中,會(huì)去判斷是否需要生成一個(gè)代理對(duì)象,如果需要就生成代理對(duì)象返回,如果不需要生成代理對(duì)象,則將原始對(duì)象返回即可。最后,把拿到手的對(duì)象存入到二級(jí)緩存中以備下次使用,同時(shí)刪除掉三級(jí)緩存中對(duì)應(yīng)的數(shù)據(jù)。這樣 AService 所依賴的 BService 就創(chuàng)建好了。

接下來(lái)繼續(xù)去完善 AService,去執(zhí)行各種后置的處理器,此時(shí),有的后置處理器想給 AService 生成代理對(duì)象,發(fā)現(xiàn) AService 已經(jīng)是代理對(duì)象了,就不用生成了,直接用已有的代理對(duì)象去代替 AService 即可。

至此,AService 和 BService 都搞定。

本質(zhì)上,singletonFactories 是把 AOP 的過(guò)程提前了。

3. 小結(jié)

總的來(lái)說(shuō),Spring 解決循環(huán)依賴把握住兩個(gè)關(guān)鍵點(diǎn):

  • 提前暴露:剛剛創(chuàng)建好的對(duì)象還沒(méi)有進(jìn)行任何賦值的時(shí)候,將之暴露出來(lái)放到緩存中,供其他 Bean 提前引用(二級(jí)緩存)。
  • 提前 AOP:A 依賴 B 的時(shí)候,去檢查是否發(fā)生了循環(huán)依賴(檢查的方式就是將正在創(chuàng)建的 A 標(biāo)記出來(lái),然后 B 需要 A,B 去創(chuàng)建 A 的時(shí)候,發(fā)現(xiàn) A 正在創(chuàng)建,就說(shuō)明發(fā)生了循環(huán)依賴),如果發(fā)生了循環(huán)依賴,就提前進(jìn)行 AOP 處理,處理完成后再使用(三級(jí)緩存)。

原本 AOP 這個(gè)過(guò)程是屬性賦完值之后,再由各種后置處理器去處理 AOP 的(AbstractAutoProxyCreator),但是如果發(fā)生了循環(huán)依賴,就先 AOP,然后屬性賦值,最后等到后置處理器執(zhí)行的時(shí)候,就不再做 AOP 的處理了。

不過(guò)需要注意,三級(jí)緩存并不能解決所有的循環(huán)依賴。

以上就是一文教你如何通過(guò)三級(jí)緩存解決Spring循環(huán)依賴的詳細(xì)內(nèi)容,更多關(guān)于三級(jí)緩存解決Spring循環(huán)依賴的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java加密解密示例分享

    java加密解密示例分享

    想要?jiǎng)?chuàng)造一個(gè)只有自己能看懂的文件嗎?那就是對(duì)數(shù)據(jù)加密吧,下面分享一個(gè)java的數(shù)據(jù)加密與解密示例
    2014-01-01
  • 淺談Java 8 新增函數(shù)式接口到底是什么

    淺談Java 8 新增函數(shù)式接口到底是什么

    這篇文章主要介紹了淺談Java 8 新增函數(shù)式接口到底是什么,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-01-01
  • SpringBoot集成Hutool防止XSS攻擊的兩種解決方法

    SpringBoot集成Hutool防止XSS攻擊的兩種解決方法

    XSS漏洞是生產(chǎn)上比較常見(jiàn)的問(wèn)題,本文主要介紹了SpringBoot集成Hutool防止XSS攻擊的兩種解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-04-04
  • Java實(shí)現(xiàn)壓縮圖片大小

    Java實(shí)現(xiàn)壓縮圖片大小

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)壓縮圖片大小,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Java編寫中容易搞錯(cuò)的一些東西

    Java編寫中容易搞錯(cuò)的一些東西

    Java編寫中容易搞錯(cuò)的一些東西...
    2006-12-12
  • Java泛型的用法及T.class的獲取過(guò)程解析

    Java泛型的用法及T.class的獲取過(guò)程解析

    這篇文章主要介紹了Java泛型的用法及T.class的獲取過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • IDEA創(chuàng)建maven項(xiàng)目時(shí)在tomcat運(yùn)行瀏覽器404的問(wèn)題

    IDEA創(chuàng)建maven項(xiàng)目時(shí)在tomcat運(yùn)行瀏覽器404的問(wèn)題

    這篇文章主要介紹了IDEA創(chuàng)建maven項(xiàng)目時(shí)在tomcat運(yùn)行瀏覽器404的問(wèn)題及解決方法,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Java中的throws關(guān)鍵字處理異常的最佳實(shí)踐記錄

    Java中的throws關(guān)鍵字處理異常的最佳實(shí)踐記錄

    在Java編程中,異常處理是保證程序健壯性和穩(wěn)定性的重要手段,除了使用try-catch塊捕獲異常外,Java還提供了throws關(guān)鍵字,允許我們將異常拋給調(diào)用者處理,本文介紹Java中的throws關(guān)鍵字處理異常的最佳實(shí)踐記錄,感興趣的朋友一起看看吧
    2025-01-01
  • SpringBoot實(shí)現(xiàn)多租戶架構(gòu)

    SpringBoot實(shí)現(xiàn)多租戶架構(gòu)

    在SpringBoot中可以通過(guò)多數(shù)據(jù)源和動(dòng)態(tài)路由來(lái)實(shí)現(xiàn)多租戶機(jī)制,本文主要介紹了SpringBoot實(shí)現(xiàn)多租戶架構(gòu),具有一定的參考價(jià)值,感興趣的可以里哦啊接一下
    2024-03-03
  • Spring?Boot項(xiàng)目中使用?TrueLicense?生成和驗(yàn)證License的詳細(xì)步驟

    Spring?Boot項(xiàng)目中使用?TrueLicense?生成和驗(yàn)證License的詳細(xì)步驟

    這篇文章主要介紹了Spring?Boot項(xiàng)目中使用?TrueLicense?生成和驗(yàn)證License,本文分步驟給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-10-10

最新評(píng)論