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

一文詳解Spring是怎樣處理循環(huán)依賴(lài)的

 更新時(shí)間:2024年01月24日 11:06:47   作者:后端開(kāi)發(fā)萌新  
循環(huán)依賴(lài)簡(jiǎn)單理解就是A,B 兩個(gè)bean相互依賴(lài),A依賴(lài)B,B依賴(lài)A,A->B、B->A大概就是這樣,這篇文章主要介紹了Spring是怎樣處理循環(huán)依賴(lài)的,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下

環(huán)境

Spring Framework Version: 5.3.x

Gradle Version:7.5.1

什么是循環(huán)依賴(lài)?

簡(jiǎn)單理解就是A,B 兩個(gè)bean相互依賴(lài),A依賴(lài)B,B依賴(lài)A

A->B、B->A大概就是這樣.

所有注入場(chǎng)景的循環(huán)依賴(lài)Spring都能解決嗎?

答案是 “不”, Spring不能夠解決循環(huán)依賴(lài)的構(gòu)造器注入,其它的注入方式都能解決

**注意:**能解決非構(gòu)造器注入的循環(huán)依賴(lài)的前提是開(kāi)啟允許循環(huán)依賴(lài)(allowCircularReferences = true),在spring中默認(rèn)開(kāi)啟,如果是在springboot2.x中,那么是默認(rèn)關(guān)閉的

場(chǎng)景

我們來(lái)編寫(xiě)一段代碼,模擬一下循環(huán)依賴(lài)場(chǎng)景

TestA.java

public class TestA {
	private TestB testB;

	public void setB(TestB testB) {
		this.testB = testB;
	}

	public void testCircularReference() {
		System.out.println("TestA bean register success...");
	}
}

TestB.java

public class TestB {
	private TestA testA;

	public void setA(TestA testA) {
		this.testA = testA;
	}

	public void testCircularReference() {
		System.out.println("TestB bean register success...");
	}
}

spring-context.xml

//使用的注入方式是自動(dòng)裝配,根據(jù)type自動(dòng)裝配
<bean id="testA" class="com.spring.demo.circularreference.TestA" autowire="byType"></bean>
<bean id="testB" class="com.spring.demo.circularreference.TestB" autowire="byType"></bean>

Main.java

public class Main {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
		TestA testA = context.getBean(TestA.class);
		TestB testB = context.getBean(TestB.class);
		testA.testCircularReference();
		testB.testCircularReference();
    }
}

測(cè)試結(jié)果

a bean register success...
b bean register success...

好,代碼編寫(xiě)完畢,測(cè)試也沒(méi)問(wèn)題,接下來(lái)我們來(lái)進(jìn)行源碼解析

Spring是怎么解決循環(huán)依賴(lài)的?

前置說(shuō)明

Spring是通過(guò)三級(jí)緩存來(lái)解決循環(huán)依賴(lài)的,我們來(lái)看一張圖片,認(rèn)識(shí)一下三級(jí)緩存

在這里插入圖片描述

一級(jí)緩存(singletonObjects):存放初始化完成的bean

二級(jí)緩存(earlySingletonObjects):存放實(shí)例化但未初始化的bean

三級(jí)緩存(singletonFactories):存放對(duì)象工廠(chǎng),也就是把實(shí)例化但未初始化的bean包裝為ObjectFactory

認(rèn)識(shí)了三級(jí)緩存后,我們來(lái)剖析一下Spring是怎么利用三級(jí)緩存來(lái)解決循環(huán)依賴(lài)的

在實(shí)例化TestA之后,我們先會(huì)把TestA添加到三級(jí)緩存,然后進(jìn)行屬性填充,給TestB進(jìn)行依賴(lài)注入,那么到TestB屬性填充時(shí),發(fā)現(xiàn)TestB也依賴(lài)TestA,這個(gè)時(shí)候去注入TestA,先從一級(jí)緩存獲取,獲取不到,再?gòu)亩?jí)緩存獲取,二級(jí)緩存也沒(méi)有,這個(gè)時(shí)候從三級(jí)緩存獲取,獲取到了,然后getObject()生成TestA對(duì)象(在這個(gè)時(shí)候,如果有代理,那么生成代理對(duì)象,如果沒(méi)有代理,直接返回原本的對(duì)象),獲取到TestA對(duì)象后移到二級(jí)緩存并返回,這個(gè)時(shí)候TestB就成功注入了TestA,然后TestB順利的走完生命周期,就回到了第一個(gè)TestA,進(jìn)行初始化,初始化完之后,再?gòu)亩?jí)緩存中取出再重新賦值(為了保證bean是同一個(gè)),最后添加到一級(jí)緩存中

這么說(shuō)可能有點(diǎn)繞,我們結(jié)合一張圖來(lái)看看

在這里插入圖片描述

大概就是這樣

  • 添加半成品的TestA(實(shí)例化但沒(méi)進(jìn)行屬性注入與初始化)到三級(jí)緩存,然后注入TestB,在TestB中也要進(jìn)行屬性注入,然后就去注入TestA
  • 在TestB中注入TestA時(shí),直接從緩存獲取,因?yàn)檫@時(shí)三級(jí)緩存已經(jīng)存在TestA,然后調(diào)用getEarlyBeanReference方法生成對(duì)象(如果有代理也是在此生成)
  • 添加到二級(jí)緩存中并刪除三級(jí)緩存,最后返回,TestB屬性注入完成,繼續(xù)走Bean的生命周期
  • 回到原本TestA,這時(shí)TestB注入已經(jīng)完成,然后初始化,最后從二級(jí)緩存中獲取最新的bean,避免不是同一個(gè)對(duì)象(代理對(duì)象)

源碼解析

addSingletonFactory

? TestA在實(shí)例化之后添加三級(jí)緩存

在這里插入圖片描述

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) 	{
            //添加三級(jí)緩存
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

getSingleton

TestB屬性注入TestA過(guò)程中,從緩存獲取TestA

在這里會(huì)獲取到早期對(duì)象,移除三級(jí)緩存中的ObjectFactory

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 從一級(jí)緩存中獲取TestA,這個(gè)時(shí)候是沒(méi)有的
    Object singletonObject = this.singletonObjects.get(beanName);
    //isSingletonCurrentlyInCreation 在實(shí)例化之前就添加了創(chuàng)建標(biāo)識(shí),所以這里為true
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        //從二級(jí)緩存中獲取TestA,這個(gè)時(shí)候二級(jí)緩存也是沒(méi)有的
        singletonObject = this.earlySingletonObjects.get(beanName);
        //allowEarlyReference 是否允許循環(huán)依賴(lài)
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                //雙重檢查
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        //從三級(jí)緩存中獲取ObjectFactory
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            //獲取早期暴露對(duì)象(代理對(duì)象也是在此生成)
                            singletonObject = singletonFactory.getObject();
                            //添加到二級(jí)緩存中
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            //根據(jù)beanName從三級(jí)緩存中移除ObjectFactory
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

getEarlyBeanReference

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        //遍歷smartInstantiationAware類(lèi)型的后處理器
        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
            //獲取早期bean引用
            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
        }
    }
    return exposedObject;
}

此時(shí)獲取到的TestA是個(gè)代理對(duì)象,那么TestB里注入的是代理對(duì)象,然后TestA成功注入TestB,但是原本的TestA還是個(gè)普通的對(duì)象,怎么辦呢?

很簡(jiǎn)單,之前不是將對(duì)象放到二級(jí)緩存了嗎,所以在TestA注入完TestB并且初始化之后,這個(gè)時(shí)候會(huì)去二級(jí)緩存中獲取最新的bean,并重新賦值,保證是同一個(gè)對(duì)象,看代碼

//二級(jí)緩存提前暴露
if (earlySingletonExposure) {
    //從二級(jí)緩存中獲取到最新的bean
    Object earlySingletonReference = getSingleton(beanName, false);
    //如果能獲取到并且exposedObject和實(shí)例化之后的bean是保持一致的,那么就進(jìn)行重新賦值
    if (earlySingletonReference != null) {
        if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
        }
        else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
                if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                    actualDependentBeans.add(dependentBean);
                }
            }
            if (!actualDependentBeans.isEmpty()) {
                throw new BeanCurrentlyInCreationException(beanName,
                                "Bean with name '" + beanName + "' has been injected into other beans [" +
                                    StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                    "] in its raw version as part of a circular reference, but has eventually been " +
                                  "wrapped. This means that said other beans do not use the final version of the " +
                                    "bean. This is often the result of over-eager type matching - consider using " +
                                    "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
            }
        }
    }
}

至此,循環(huán)依賴(lài)就解析完畢了,這里有個(gè)需要注意的點(diǎn),就是生成代理對(duì)象那一塊,需要依賴(lài)于SmartInstantiationAwareBeanPostProcessor生成代理的,是不能被處理的,例如@Async注解,它需要依賴(lài)于SmartInstantiationAwareBeanPostProcessor,被它代理的類(lèi),生成代理時(shí)機(jī)是初始化bean之后,那么如果在循環(huán)依賴(lài)?yán)锍霈F(xiàn),例如TestA、TestB互相依賴(lài),那么TestA使用了@Async注解,那么它的代理生成時(shí)機(jī)在bean的初始化之后,這樣就會(huì)出現(xiàn)問(wèn)題了,在TestB注入TestA時(shí),從緩存中獲取TestA,這時(shí)是沒(méi)有被代理的,當(dāng)原始的TestA注入完成后,在初始化之后生成代理,這個(gè)時(shí)候就會(huì)造成TestB里注入的TestA不是代理對(duì)象,而原始的TestA已經(jīng)變成代理對(duì)象了,就會(huì)造成不是同一個(gè)對(duì)象

總結(jié)

看完源碼之后,相信大家都有了一些了解,如果看完還是不太明白也沒(méi)關(guān)系,自己跟著debug一遍然后做總結(jié),加深印象,接下來(lái)我們對(duì)以上做一下總結(jié)吧。

如果被問(wèn)到Spring是如何解決循環(huán)依賴(lài)的?

答: Spring是通過(guò)三級(jí)緩存去解決的循環(huán)依賴(lài),具體來(lái)說(shuō)就是在TestA實(shí)例化之后,屬性填充之前,把Test包裝成ObjectFactory對(duì)象并存入三級(jí)緩存中,這時(shí)注入TestB,然后在TestB里注入TestA時(shí),就會(huì)從三級(jí)緩存里getObject,取出TestA半成品對(duì)象(如果是代理對(duì)象就進(jìn)行創(chuàng)建),并且配合二級(jí)緩存,把它存入二級(jí)緩存中并在三級(jí)緩存中刪除,最后回到原始TestA,在初始化原TestA之后,進(jìn)行重新賦值,避免不是同一個(gè)對(duì)象。

為什么構(gòu)造器注入不能解決循環(huán)依賴(lài)?

**答:**因?yàn)闃?gòu)造器注入是在實(shí)例化bean的時(shí)候,這時(shí)候三級(jí)緩存還沒(méi)有添加,所以不能解決循環(huán)依賴(lài)。

為什么要設(shè)計(jì)三級(jí)緩存,一級(jí)、二級(jí)緩存行不行?

這個(gè)問(wèn)題非常值得思考,不過(guò)不要陷入其中,大家可以思考一下

以上就是一文詳解Spring是怎樣處理循環(huán)依賴(lài)的的詳細(xì)內(nèi)容,更多關(guān)于Spring處理循環(huán)依賴(lài)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java編程基礎(chǔ)之模仿用戶(hù)登錄代碼分享

    java編程基礎(chǔ)之模仿用戶(hù)登錄代碼分享

    這篇文章主要介紹了java編程基礎(chǔ)之模仿用戶(hù)登錄代碼分享,小編覺(jué)得挺不錯(cuò)的,這里分享給大家,供需要的朋友參考。
    2017-10-10
  • Java依賴(lài)注入容器超詳細(xì)全面講解

    Java依賴(lài)注入容器超詳細(xì)全面講解

    依賴(lài)注入(Dependency Injection)和控制反轉(zhuǎn)(Inversion of Control)是同一個(gè)概念。具體含義是:當(dāng)某個(gè)角色(可能是一個(gè)Java實(shí)例,調(diào)用者)需要另一個(gè)角色(另一個(gè)Java實(shí)例,被調(diào)用者)的協(xié)助時(shí),在 傳統(tǒng)的程序設(shè)計(jì)過(guò)程中,通常由調(diào)用者來(lái)創(chuàng)建被調(diào)用者的實(shí)例
    2023-01-01
  • Java使用modbus-master-tcp實(shí)現(xiàn)modbus tcp通訊

    Java使用modbus-master-tcp實(shí)現(xiàn)modbus tcp通訊

    這篇文章主要為大家詳細(xì)介紹了另外一種Java語(yǔ)言的modbux tcp通訊方案,那就是modbus-master-tcp,文中的示例代碼講解詳細(xì),需要的可以了解下
    2023-12-12
  • 在IDEA中配置Selenium和WebDriver的具體操作

    在IDEA中配置Selenium和WebDriver的具體操作

    在自動(dòng)化測(cè)試領(lǐng)域Selenium是一款非常流行的開(kāi)源工具,它支持多種瀏覽器,并提供了豐富的API供開(kāi)發(fā)者使用,而WebDriver則是Selenium的一個(gè)重要組件,它負(fù)責(zé)驅(qū)動(dòng)瀏覽器執(zhí)行測(cè)試腳本,這篇文章主要給大家介紹了在IDEA中配置Selenium和WebDriver的具體操作,需要的朋友可以參考下
    2024-10-10
  • 使用IntelliJ IDEA 進(jìn)行代碼對(duì)比的方法(兩種方法)

    使用IntelliJ IDEA 進(jìn)行代碼對(duì)比的方法(兩種方法)

    這篇文章給大家?guī)?lái)了兩種IntelliJ IDEA 進(jìn)行代碼對(duì)比的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • Java利用DFA算法實(shí)現(xiàn)敏感詞管理

    Java利用DFA算法實(shí)現(xiàn)敏感詞管理

    我們?cè)谕瓿梢恍┪恼掳l(fā)布的功能是,可以使用第三方工具如阿里云的內(nèi)容安全來(lái)進(jìn)行文章的審核,但是這個(gè)第三方接口并不能過(guò)濾一些敏感詞,所以本文就來(lái)講講如何通過(guò)DFA算法就行敏感詞管理吧
    2024-11-11
  • java Long==Long有趣的現(xiàn)象詳解

    java Long==Long有趣的現(xiàn)象詳解

    這篇文章主要給大家介紹了關(guān)于java Long==Long有趣的現(xiàn)象的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • 基于java中stack與heap的區(qū)別,java中的垃圾回收機(jī)制的相關(guān)介紹

    基于java中stack與heap的區(qū)別,java中的垃圾回收機(jī)制的相關(guān)介紹

    本篇文章小編將為大家介紹,基于java中stack與heap的區(qū)別,java中的垃圾回收機(jī)制的相關(guān)介紹,需要的可以參考一下
    2013-04-04
  • 通過(guò)spring注解開(kāi)發(fā),簡(jiǎn)單測(cè)試單例和多例區(qū)別

    通過(guò)spring注解開(kāi)發(fā),簡(jiǎn)單測(cè)試單例和多例區(qū)別

    這篇文章主要介紹了通過(guò)spring注解開(kāi)發(fā),簡(jiǎn)單測(cè)試單例和多例區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 從千千靜聽(tīng)歌詞服務(wù)器獲取lrc歌詞示例分享

    從千千靜聽(tīng)歌詞服務(wù)器獲取lrc歌詞示例分享

    這篇文章主要介紹了使用PHP從千千靜聽(tīng)歌詞服務(wù)器獲取lrc歌詞的方法,大家參考使用吧
    2014-01-01

最新評(píng)論