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

Spring的循環(huán)依賴、三級(jí)緩存解決方案源碼詳細(xì)解析

 更新時(shí)間:2024年01月11日 09:50:10   作者:好奇的7號(hào)  
這篇文章主要介紹了Spring的循環(huán)依賴、三級(jí)緩存解決方案源碼詳細(xì)解析,在Spring中,由于IOC的控制反轉(zhuǎn),創(chuàng)建對(duì)象不再是簡(jiǎn)單的new出來,而是交給Spring去創(chuàng)建,會(huì)經(jīng)歷一系列Bean的生命周期才創(chuàng)建出相應(yīng)的對(duì)象,需要的朋友可以參考下

Bean的生命周期

在Spring中,由于IOC的控制反轉(zhuǎn),創(chuàng)建對(duì)象不再是簡(jiǎn)單的new出來,而是交給Spring去創(chuàng)建,會(huì)經(jīng)歷一系列Bean的生命周期才創(chuàng)建出相應(yīng)的對(duì)象。

而循環(huán)依賴問題也是由Bean的生命周期過程導(dǎo)致的問題,因此我們首先需要了解Bean的生命周期。

Bean的生命周期可以概括為4步:

實(shí)例化----屬性注入----初始化----銷毀

詳細(xì)的講,步驟如下:

實(shí)例化

1.定位:Spring容器會(huì)根據(jù)配置文件(如XML、注解等)或編程式配置來確定需要?jiǎng)?chuàng)建的Bean。

2.加載:Spring容器會(huì)加載配置文件并解析其中的Bean定義,將其轉(zhuǎn)換為內(nèi)部數(shù)據(jù)結(jié)構(gòu),例如BeanDefinition。

3.實(shí)例化:在實(shí)例化階段,Spring容器會(huì)根據(jù)Bean定義中的信息創(chuàng)建Bean的實(shí)例。這個(gè)過程可以通過構(gòu)造函數(shù)實(shí)例化、工廠方法實(shí)例化或者通過反射機(jī)制來實(shí)現(xiàn)。

屬性注入

4.屬性注入:在實(shí)例化Bean之后,Spring容器會(huì)對(duì)Bean進(jìn)行屬性注入。這可以通過setter方法注入、構(gòu)造函數(shù)參數(shù)注入或字段注入等方式來完成。

初始化

5.Aware接口回調(diào):如果Bean實(shí)現(xiàn)了Spring的Aware接口,容器會(huì)通過回調(diào)方式將一些特殊的資源注入到Bean中。例如,如果Bean實(shí)現(xiàn)了BeanFactoryAware接口,容器會(huì)將當(dāng)前的BeanFactory實(shí)例注入到Bean中。

6.初始化前回調(diào):如果Bean實(shí)現(xiàn)了InitializingBean接口,容器會(huì)在初始化之前調(diào)用它的afterPropertiesSet()方法,給Bean一個(gè)機(jī)會(huì)執(zhí)行一些初始化操作。同時(shí),Spring容器還支持使用自定義的初始化方法。

7.初始化后回調(diào):如果Bean配置了初始化回調(diào)方法,容器會(huì)調(diào)用該方法進(jìn)行一些自定義的初始化邏輯處理。

8.后置處理器(BeanPostProcessor):Spring容器會(huì)調(diào)用注冊(cè)的Bean后置處理器對(duì)Bean進(jìn)行加工和增強(qiáng)。例如,可以通過AOP技術(shù)在這個(gè)階段為Bean動(dòng)態(tài)生成代理對(duì)象。

9.完成:至此,Bean已經(jīng)成功創(chuàng)建,并且已經(jīng)完成了所有的初始化過程。此時(shí)可以將Bean提供給其他對(duì)象使用。

銷毀

10.銷毀前回調(diào)(PreDestroy):在容器關(guān)閉之前,調(diào)用Bean的銷毀前回調(diào)方法,執(zhí)行一些清理操作和釋放資源的任務(wù)。

11.銷毀:容器關(guān)閉時(shí),銷毀所有Bean實(shí)例,包括調(diào)用相應(yīng)Bean的銷毀方法,進(jìn)行最終的清理和資源釋放。

1、循環(huán)依賴問題

例如下面的代碼,A和B類就構(gòu)成了循環(huán)依賴,原因如下:

@Component
public class A {
   
   @Autowired
   private B b;
}
@Component
public class B{
 
   @Autowired
   private A a;
}

創(chuàng)建Bean的步驟:

  1. Spring 掃描 class 得到 BeanDefinition;
  2. 根據(jù)得到的 BeanDefinition 去生成 bean;
  3. 首先根據(jù) class 推斷構(gòu)造方法;
  4. 根據(jù)推斷出來的構(gòu)造方法,反射,得到一個(gè)對(duì)象(我們稱為原始對(duì)象);
  5. 填充原始對(duì)象中的屬性(依賴注入);
  6. 如果原始對(duì)象中的某個(gè)方法被 AOP 了,那么則需要根據(jù)原始對(duì)象生成一個(gè)代理對(duì)象;
  7. 把最終生成的代理對(duì)象放入單例池(源碼中叫做 singletonObjects)中,下次 getBean 時(shí)就直接從單例池拿即可;

對(duì)于上述步驟的第4步,得到原始對(duì)象后需要注入屬性,A 類中存在一個(gè) B 類的 b 屬性,此時(shí)就會(huì)根據(jù) b 屬性的類型和屬性名去 BeanFactory 中去獲取 B 類所對(duì)應(yīng)的單例bean

如果此時(shí) B 類在 BeanFactory 中還沒有生成對(duì)應(yīng)的 Bean,那么就需要去生成,就會(huì)經(jīng)過 B 的 Bean 的生命周期,也就會(huì)同樣的,需要A類的Bean,就發(fā)生了循環(huán)依賴,導(dǎo)致A和B的bean都創(chuàng)建不出來。

概括而言: A Bean創(chuàng)建–>依賴了 B 屬性–>觸發(fā) B Bean創(chuàng)建—>B 依賴了 A 屬性—>需要 A Bean(但A Bean還在創(chuàng)建過程中)

然而實(shí)際上,Spring通過三級(jí)緩存的方式自動(dòng)解決了這個(gè)問題。

2、三級(jí)緩存的引入

2.1 非AOP情況下的解決方案

根據(jù)上文的分析我們發(fā)現(xiàn),出現(xiàn)循環(huán)依賴的根本原因,是B的Bean需要注入A屬性的時(shí)候,Bean A還沒有創(chuàng)建出來,導(dǎo)致的。

那么相應(yīng)的,只要: 在進(jìn)行依賴注入之前,先把 A 的原始 Bean 放入緩存提早暴露,只要放到緩存了,其他 Bean 需要時(shí)就可以從緩存中拿了,這個(gè)緩存就應(yīng)該是earlySingletonObjects),放入緩存后,再進(jìn)行依賴注入。

由于提前暴露,在創(chuàng)建B的Bean過程中,當(dāng)需要注入A的屬性時(shí),就可以從緩存中拿到A提前暴露的原始對(duì)象(還不是最終Bean),就解決了問題。

關(guān)鍵在于全程只有一個(gè)A的原始對(duì)象,其后續(xù)的生命周期沒有變化。

如下圖所示:

2.2 三級(jí)緩存具體

因此,對(duì)于不同時(shí)期的Bean,如原始Bean、完整周期的Bean,需要不同的緩存來存放,底層源碼中有三級(jí)緩存:

/** Cache of singleton objects: bean name –> bean instance */
private final Map singletonObjects = new ConcurrentHashMap(256);
 
/** Cache of singleton factories: bean name –> ObjectFactory */
private final Map> singletonFactories = new HashMap>(16);
 
/** Cache of early singleton objects: bean name –> bean instance */
private final Map earlySingletonObjects = new HashMap(16);
  • 一級(jí)緩存:singletonObjects;緩存的是已經(jīng)經(jīng)歷了完整生命周期的bean對(duì)象。
  • 二級(jí)緩存:earlySingletonObjects;比 singletonObjects 多了一個(gè) early ,表示緩存的是早期的 bean對(duì)象(原始對(duì)象)。早期指的是 Bean 的生命周期還沒走完就把這個(gè) Bean 放入了 earlySingletonObjects
  • 三級(jí)緩存:singletonFactories;緩存的是 ObjectFactory,表示對(duì)象工廠,用來創(chuàng)建某個(gè)對(duì)象的。

3、有AOP情況下使用singletonFactories

3.1 引入三級(jí)緩存

看似我們只需要1、2級(jí)緩存就能夠解決問題了,為什么需要三級(jí)緩存呢? 這就需要考慮到AOP代理對(duì)象的問題了:

上文的紅字提到,之所以能夠提前暴露,是因?yàn)榧俣ǖ腁的原始對(duì)象始終是同一個(gè)對(duì)象,但如果有AOP的情況下呢?我們考慮這樣的場(chǎng)景:

按照上文的分析,假設(shè)創(chuàng)建B的bean過程中,注入了A的原始對(duì)象屬性。

然后,A的原始對(duì)象采用AOP產(chǎn)生了一個(gè)代理對(duì)象,即,A的Bean變成了AOP 之后的代理對(duì)象。而B中的 屬性a對(duì)應(yīng)的并不是 AOP 之后的代理對(duì)象,而仍然是原始對(duì)象。

也就是說,這種情況下,B 依賴的 A 和最終的 A 不是同一個(gè)對(duì)象!

而解決這個(gè)問題的方法,就是引入三級(jí)緩存的singletonFactories

3.2 三級(jí)緩存具體解析

實(shí)際上,在有AOP的情況下,Spring并沒有像第2節(jié)所說,直接將示例緩存到二級(jí)緩存,而是生成完原始對(duì)象之后”多此一舉“地將實(shí)例先封裝到objectFactory中,在需要引用的時(shí)候再通過singletonFactory.getObject()獲取。

跟進(jìn)getObject()方法,其實(shí)執(zhí)行了getEarlyBeanReference這個(gè)關(guān)鍵方法。

    this.addSingletonFactory(beanName, () -> {
		return this.getEarlyBeanReference(beanName, mbd, bean);
	});

也就是說,Spring將當(dāng)前bean緩存到earlyProxyReferences中,標(biāo)識(shí)提前曝光的bean。而wrapIfNecessary是用于Spring AOP自動(dòng)代理的,也就是說在被提前引用前,進(jìn)行了AOP代理,并得到了代理對(duì)象。 此時(shí)earlySingletonObjects緩存中的對(duì)象就是代理對(duì)象了!

因此,假設(shè)此時(shí)有其他對(duì)象依賴了A,就可以從earlySingletonObjects中獲取到A原始對(duì)象的代理對(duì)象了,并且和A是同一個(gè)對(duì)象,實(shí)現(xiàn)了目標(biāo)。

3.3 后續(xù)依賴問題

當(dāng) B 創(chuàng)建完了之后,A 繼續(xù)進(jìn)行生命周期,而 A 在完成屬性注入后,會(huì)按照它本身的邏輯去進(jìn)行AOP,而此時(shí)我們知道 A 原始對(duì)象已經(jīng)經(jīng)歷過了 AOP ,所以對(duì)于 A 本身而言,不會(huì)再去進(jìn)行 AOP了,那么怎么判斷一個(gè)對(duì)象是否經(jīng)歷過了 AOP 呢?

注意postProcessAfterInitialization方法,會(huì)當(dāng)前beanName是否在earlyProxyReferences中,如果在就AOP過了,不在則執(zhí)行AOP方法。

此時(shí)對(duì)于Bean A對(duì)象而言已經(jīng)完成創(chuàng)建了,可以把它放入緩存singletonObjects中了,因此從earlySingletonObjects 中得到代理對(duì)象,然后入 singletonObjects 中。

至此,整個(gè)循環(huán)依賴解決完畢。

4、總結(jié)

這里用圖來說明具體流程:

Spring AOP循環(huán)依賴

對(duì)于三級(jí)緩存的singletonFactories,總結(jié)而言:

緩存的是一個(gè) ObjectFactory ,主要用來去生成原始對(duì)象進(jìn)行了 AOP之后得到的「代理對(duì)象」。

在每個(gè) Bean 的生成過程中,都會(huì)提前暴露一個(gè)工廠,這個(gè)工廠可能用到,也可能用不到:

(1)如果沒有出現(xiàn)循環(huán)依賴依賴本 bean,那么這個(gè)工廠無用,本 bean 按照自己的生命周期執(zhí)行,執(zhí)行完后直接把本 bean 放入 singletonObjects 中即可(對(duì)應(yīng)本文章的第1節(jié))

(2)如果出現(xiàn)了循環(huán)依賴依賴了本 bean,則:

  • 如果有 AOP 的話,另外那個(gè) bean 執(zhí)行 ObjectFactory 提交得到一個(gè) AOP 之后的代理對(duì)象。(對(duì)應(yīng)本文章第3節(jié))
  • 如果無需 AOP ,則直接得到一個(gè)原始對(duì)象。(對(duì)應(yīng)本文章第2節(jié))

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

相關(guān)文章

  • java郵件通知工具類

    java郵件通知工具類

    這篇文章主要為大家詳細(xì)介紹了java郵件通知工具類,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • Spring Boot 整合單機(jī)websocket的步驟 附github源碼

    Spring Boot 整合單機(jī)websocket的步驟 附github源碼

    websocket 是一個(gè)通信協(xié)議,通過單個(gè) TCP 連接提供全雙工通信,這篇文章主要介紹了Spring Boot 整合單機(jī)websocket的步驟(附github源碼),需要的朋友可以參考下
    2021-10-10
  • Mac使用Idea配置傳統(tǒng)SSM項(xiàng)目(非maven項(xiàng)目)

    Mac使用Idea配置傳統(tǒng)SSM項(xiàng)目(非maven項(xiàng)目)

    本文主要介紹了Mac使用Idea配置傳統(tǒng)SSM項(xiàng)目(非maven項(xiàng)目),將展示如何設(shè)置項(xiàng)目結(jié)構(gòu)、添加依賴關(guān)系等,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • 詳解spring boot集成RabbitMQ

    詳解spring boot集成RabbitMQ

    RabbitMQ作為AMQP的代表性產(chǎn)品,在項(xiàng)目中大量使用。結(jié)合現(xiàn)在主流的spring boot,極大簡(jiǎn)化了開發(fā)過程中所涉及到的消息通信問題。
    2017-03-03
  • Spring MVC 中 AJAX請(qǐng)求并返回JSON的示例

    Spring MVC 中 AJAX請(qǐng)求并返回JSON的示例

    本篇文章主要介紹了Spring MVC 中 AJAX請(qǐng)求并返回JSON,具有一定的參考價(jià)值,有興趣的可以了解一下。
    2017-01-01
  • SpringMVC記錄我遇到的坑_AOP注解無效,切面不執(zhí)行的解決

    SpringMVC記錄我遇到的坑_AOP注解無效,切面不執(zhí)行的解決

    這篇文章主要介紹了SpringMVC記錄我遇到的坑_AOP注解無效,切面不執(zhí)行的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Kafka源碼系列教程之刪除topic

    Kafka源碼系列教程之刪除topic

    這篇文章主要給大家介紹了關(guān)于Kafka源碼系列教程之刪除topic的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • SpringBoot通過RedisTemplate執(zhí)行Lua腳本的方法步驟

    SpringBoot通過RedisTemplate執(zhí)行Lua腳本的方法步驟

    這篇文章主要介紹了SpringBoot通過RedisTemplate執(zhí)行Lua腳本的方法步驟,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • 關(guān)于maven本地倉(cāng)庫(kù)的配置方式

    關(guān)于maven本地倉(cāng)庫(kù)的配置方式

    這篇文章主要介紹了關(guān)于maven本地倉(cāng)庫(kù)的配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • 如何通過Java實(shí)現(xiàn)PDF轉(zhuǎn)高質(zhì)量圖片

    如何通過Java實(shí)現(xiàn)PDF轉(zhuǎn)高質(zhì)量圖片

    在Java中,將PDF文件轉(zhuǎn)換為高質(zhì)量的圖片可以使用不同的庫(kù),其中最常用的庫(kù)之一是?Apache?PDFBox,下面我們就來看看這個(gè)庫(kù)的具體使用吧
    2024-10-10

最新評(píng)論