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

Java中通過三級(jí)緩存解決Spring循環(huán)依賴詳解

 更新時(shí)間:2023年09月09日 14:53:48   作者:_江南一點(diǎn)雨  
這篇文章主要介紹了Java中通過三級(jí)緩存解決Spring循環(huán)依賴詳解,當(dāng)出現(xiàn)兩個(gè)或多個(gè) Bean 在初始化時(shí)相互依賴的情況時(shí),Spring Boot 會(huì)將其中一個(gè) Bean 提前暴露出來,以便其他 Bean 能夠在初始化時(shí)正確地引用它,這一策略能有效避免循環(huán)依賴導(dǎo)致的問題,需要的朋友可以參考下

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)依賴的類型

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

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

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

還有自我依賴,如下圖:

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

另外,Spring 并不能處理所有的循環(huán)依賴,后面松哥會(huì)和大家進(jìn)行分析。

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

2.1 解決思路

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

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

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

接下來我們就需要給 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í)就先從緩存池中取出來 AService 先用著,然后繼續(xù) BService 創(chuàng)建的后續(xù)流程,直到 BService 創(chuàng)建完成后,將之賦值給 AService,此時(shí) AService 和 BService 就都創(chuàng)建完成了。

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

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

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

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

2.2 存在 AOP 怎么辦

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

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

正常來說是我們首先通過反射獲取到一個(gè) Bean 的實(shí)例,然后就是給這個(gè) Bean 填充屬性,屬性填充完畢之后,接下來就是執(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è)賮砘仡櫹旅孢@張圖:

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

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

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

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

  1. 首先向三級(jí)緩存中添加一條記錄,記錄的 key 就是當(dāng)前 Bean 的 beanName,value 則是一個(gè) Lambda 表達(dá)式 ObjectFactory,通過執(zhí)行這個(gè) Lambda 可以給當(dāng)前 AService 生成代理對(duì)象。
  2. 然后如果二級(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,如果有,就使用,如果沒有,則去二級(jí)緩存中查找是否有 AService,如果有,就使用,如果沒有,則去三級(jí)緩存中找出來那個(gè) ObjectFactory,然后執(zhí)行這里的 getObject 方法,這個(gè)方法在執(zhí)行的過程中,會(huì)去判斷是否需要生成一個(gè)代理對(duì)象,如果需要就生成代理對(duì)象返回,如果不需要生成代理對(duì)象,則將原始對(duì)象返回即可。最后,把拿到手的對(duì)象存入到二級(jí)緩存中以備下次使用,同時(shí)刪除掉三級(jí)緩存中對(duì)應(yīng)的數(shù)據(jù)。這樣 AService 所依賴的 BService 就創(chuàng)建好了。

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

至此,AService 和 BService 都搞定。

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

3. 小結(jié)

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

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

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

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

到此這篇關(guān)于Java中通過三級(jí)緩存解決Spring循環(huán)依賴詳解的文章就介紹到這了,更多相關(guān)三級(jí)緩存解決Spring循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java利用TCP協(xié)議實(shí)現(xiàn)客戶端與服務(wù)器通信(附通信源碼)

    Java利用TCP協(xié)議實(shí)現(xiàn)客戶端與服務(wù)器通信(附通信源碼)

    這篇文章主要介紹了Java利用TCP協(xié)議實(shí)現(xiàn)客戶端與服務(wù)器通信(附通信源碼),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • SpringBoot項(xiàng)目加入沖突動(dòng)態(tài)監(jiān)測(cè)算法的實(shí)現(xiàn)

    SpringBoot項(xiàng)目加入沖突動(dòng)態(tài)監(jiān)測(cè)算法的實(shí)現(xiàn)

    沖突動(dòng)態(tài)監(jiān)測(cè)算法是一種網(wǎng)絡(luò)通信中的沖突檢測(cè)方法,適用于無線網(wǎng)絡(luò)或其他共享傳輸介質(zhì)的環(huán)境,本文主要介紹了SpringBoot項(xiàng)目加入沖突動(dòng)態(tài)監(jiān)測(cè)算法的實(shí)現(xiàn),感興趣的可以了解一下
    2023-09-09
  • springboot整合阿里云百煉DeepSeek實(shí)現(xiàn)sse流式打印的操作方法

    springboot整合阿里云百煉DeepSeek實(shí)現(xiàn)sse流式打印的操作方法

    這篇文章主要介紹了springboot整合阿里云百煉DeepSeek實(shí)現(xiàn)sse流式打印,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2025-04-04
  • Spring @Transactional工作原理詳解

    Spring @Transactional工作原理詳解

    這篇文章主要介紹了Spring @Transactional工作原理詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • java實(shí)現(xiàn)后臺(tái)返回base64圖形編碼

    java實(shí)現(xiàn)后臺(tái)返回base64圖形編碼

    這篇文章主要介紹了java實(shí)現(xiàn)后臺(tái)返回base64圖形編碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • IDEA+Maven搭建JavaWeb項(xiàng)目的方法步驟

    IDEA+Maven搭建JavaWeb項(xiàng)目的方法步驟

    本文主要介紹了IDEA+Maven搭建JavaWeb項(xiàng)目的方法步驟,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Java漢字轉(zhuǎn)拼音工具類完整代碼實(shí)例

    Java漢字轉(zhuǎn)拼音工具類完整代碼實(shí)例

    這篇文章主要介紹了java漢字轉(zhuǎn)拼音工具類完整代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • IDEA編譯報(bào)錯(cuò):Error:java:無效的源發(fā)行版:17的解決辦法

    IDEA編譯報(bào)錯(cuò):Error:java:無效的源發(fā)行版:17的解決辦法

    IDEA里面裝了幾個(gè)版本的JDK,導(dǎo)入工程后時(shí)不時(shí)提示一下錯(cuò)誤,下面這篇文章主要給大家介紹了關(guān)于IDEA編譯報(bào)錯(cuò):Error:java:無效的源發(fā)行版:17的解決辦法,需要的朋友可以參考下
    2023-01-01
  • MyBatis常用動(dòng)態(tài)sql大總結(jié)

    MyBatis常用動(dòng)態(tài)sql大總結(jié)

    這篇文章主要給大家介紹了關(guān)于MyBatis常用動(dòng)態(tài)sql的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • 如何利用Ganymed SSH-2模擬SSH操作

    如何利用Ganymed SSH-2模擬SSH操作

    這幾天看SFTP資料時(shí),無意中看到了Ganymed SSH-2,寫了個(gè)簡(jiǎn)單demo,通過,感覺挺好用的,下面就和大家分享下。需要的朋友可以過來參考參考
    2013-08-08

最新評(píng)論