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

Spring循環(huán)依賴產(chǎn)生與解決

 更新時(shí)間:2022年12月20日 10:46:43   作者:碼畜c  
Spring的解決循環(huán)依賴是有前置條件的,要解決循環(huán)依賴我們首先要了解Spring Bean對(duì)象的創(chuàng)建過(guò)程和依賴注入的方式。依賴注入方式,我之前的博客有所分享,大家可以在看本篇文章之前進(jìn)行一下小小的回顧

循環(huán)依賴產(chǎn)生情景

探討如何解決循環(huán)依賴之前,更應(yīng)該思考清楚什么情況下會(huì)發(fā)生這種問(wèn)題?

1、模擬Prototype Bean的循環(huán)依賴

static class BeanA {
    // 1. 屬性循環(huán)依賴
    BeanB beanB = new BeanB();
    // 2. 構(gòu)造器循環(huán)依賴
    public BeanA(BeanB beanB) {
        this.beanB = beanB;
    }
}
static class BeanB {
    // 1. 屬性循環(huán)依賴
    BeanA beanA = new BeanA();
    // 2. 構(gòu)造器循環(huán)依賴
    public BeanB(BeanA beanA) {
        this.beanA = beanA;
    }
}
public static void main(String[] args) {
    // 1. 屬性循環(huán)依賴
    BeanA beanA = new BeanA(); // StackOverflowError
    // 2. 構(gòu)造器循環(huán)依賴
    new BeanA(new BeanB(new BeanA(new BeanB( /*....*/ ))));
}

Prototype bean,構(gòu)造器注入Bean時(shí),強(qiáng)調(diào)注入的是成品Bean,無(wú)法解決循環(huán)依賴問(wèn)題。

Prototype bean,屬性上注入Bean時(shí),由于原型模式是單個(gè)bean可以被多次創(chuàng)建的,一旦發(fā)生循環(huán)依賴,就會(huì)產(chǎn)生上面這種不斷在創(chuàng)建新的BeanA與BeanB導(dǎo)致的棧溢出。源碼中的解決方式:記錄并檢測(cè),如下:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    // ...
    // Fail if we're already creating this bean instance:
    // We're assumably within a circular reference.
    if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
    // ...
    else if (mbd.isPrototype()) {
        // It's a prototype -> create a new instance.
        Object prototypeInstance = null;
        try {
            beforePrototypeCreation(beanName);
            prototypeInstance = createBean(beanName, mbd, args);
        }
        finally {
            afterPrototypeCreation(beanName);
        }
        bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    }
    // ...
}
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    return (curVal != null &&
            (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}
protected void beforePrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal == null) {
        this.prototypesCurrentlyInCreation.set(beanName);
    }
    else if (curVal instanceof String) {
        Set<String> beanNameSet = new HashSet<>(2);
        beanNameSet.add((String) curVal);
        beanNameSet.add(beanName);
        this.prototypesCurrentlyInCreation.set(beanNameSet);
    }
    else {
        Set<String> beanNameSet = (Set<String>) curVal;
        beanNameSet.add(beanName);
    }
}

獲取Bean前先判斷是否當(dāng)前Bean是否為Prototype并且在創(chuàng)建中。如果不是,且當(dāng)前Bean為Prototype,那么會(huì)記錄當(dāng)前Bean正在創(chuàng)建中(通過(guò)beforePrototypeCreation方法)。

以模擬循環(huán)依賴代碼為例:

  • 創(chuàng)建BeanA
  • populateBean(BeanA)
  • 創(chuàng)建BeanB
  • populateBean(BeanB)
  • 創(chuàng)建BeanA,isPrototypeCurrentlyInCreation發(fā)現(xiàn)BeanA正常創(chuàng)建中,拋出BeanCurrentlyInCreationException

2、模擬Singleton Bean的循環(huán)依賴

static class BeanA {
    BeanB beanB;
    public BeanA(BeanB beanB) {
        this.beanB = beanB;
    }
}
static class BeanB {
    BeanA beanA;
    public BeanB(BeanA beanA) {
        this.beanA = beanA;
    }
}
public static void main(String[] args) {
    // 1. 屬性循環(huán)依賴
    BeanA beanA = new BeanA();
    BeanB beanB = new BeanB();
    beanA.beanB = beanB;
    beanB.beanA = beanA;
    // 2. 構(gòu)造器循環(huán)依賴
    new BeanA(new BeanB(new BeanA(new BeanB( /*....*/ ))));
}

Singleton bean,構(gòu)造器注入Bean時(shí),強(qiáng)調(diào)注入的是成品Bean,無(wú)法解決循環(huán)依賴問(wèn)題。

Singleton bean,屬性上注入Bean時(shí),允許注入提前暴露的半成品的Bean,即沒(méi)有填充屬性&初始化的Bean,只要引用關(guān)系能關(guān)聯(lián)上,屬性填充與初始化隨著程序的執(zhí)行自然就會(huì)處理完成,可以解決循環(huán)依賴。

為什么構(gòu)造器中不能先傳遞一個(gè)半成品Bean,然后賦值給成員Bean屬性呢?而一定要傳遞一個(gè)成品Bean?很好理解,spring并不能確定用戶在構(gòu)造器中的操作僅為賦值給Bean屬性。且很多時(shí)候,我們需要通過(guò)bean的一些注入的Bean屬性來(lái)執(zhí)行一些業(yè)務(wù)操作的。如果為空,那么就可能會(huì)發(fā)生空指針異常。而且這樣也不是特別合理。若用戶選擇屬性注入的方式,用戶不會(huì)涉及這些操作,那么自然就不需要考慮這些問(wèn)題。

3、總結(jié)成一句話來(lái)說(shuō):spring僅能解決單例Bean下發(fā)生在屬性上注入Bean產(chǎn)生的循環(huán)依賴。

Spring如何解決循環(huán)依賴

通過(guò)三級(jí)緩存解決循環(huán)依賴,分別是:

1、singletonObjects: 一級(jí)緩存,存儲(chǔ)成品Bean

2、earlySingletonObjects: 二級(jí)緩存,存儲(chǔ)半成品Bean

3、singletonFactories: 三級(jí)緩存,存儲(chǔ)生成半成品Bean的ObjectFactory的lambda

還是以BeanA注入BeanB,BeanB注入BeanA為例,描述一下大致的執(zhí)行流程:

  • 檢查BeanA的緩存信息
  • 反射實(shí)例化BeanA
  • 注冊(cè)暴露 BeanA的lambda(生成放入二級(jí)緩存中的半成品BeanA)到三級(jí)緩存:addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))
  • 填充屬性BeanB:populateBean
  • 檢查BeanB的緩存信息
  • 注冊(cè)暴露 BeanB的lambda(生成放入二級(jí)緩存中的半成品BeanB)到三級(jí)緩存
  • 反射實(shí)例化BeanB
  • 填充屬性BeanA:populateBean
  • 檢查BeanA的緩存信息,從三級(jí)緩存中獲取到了生成半成品BeanA的lambda,執(zhí)行獲取半成品BeanA并放入二級(jí)緩存,并在三級(jí)緩存中移除lambda
  • 將生成的BeanA的二級(jí)緩存對(duì)象賦值到BeanB的屬性上
  • 將BeanB放入一級(jí)緩存,并在二、三級(jí)緩存中清理關(guān)于BeanB的記錄
  • 初始化BeanB
  • 返回到第四步,將成品BeanB賦值到BeanA的屬性上
  • 將BeanA放入一級(jí)緩存,并在二、三級(jí)緩存中清理關(guān)于BeanA的記錄
  • 初始化BeanA

思考一下,是否能通過(guò)二級(jí)緩存來(lái)解決循環(huán)依賴?

首先spring對(duì)于三級(jí)緩存的應(yīng)用,就是在生成需要提前暴露的半成品Bean,要么返回的是通過(guò)反射實(shí)例化后的對(duì)象,要么是被一組SmartInstantiationAwareBeanPostProcessor處理后的對(duì)象(比如生成代理Bean),然后在放到二級(jí)緩存中。那么我們?cè)诜湃攵?jí)緩存時(shí),不通過(guò)三級(jí)緩存獲取這個(gè)過(guò)程,直接在方法中復(fù)現(xiàn)這個(gè)過(guò)程,在放入二級(jí)緩存中,效果想必也是相同的。

到此這篇關(guān)于Spring循環(huán)依賴產(chǎn)生與解決的文章就介紹到這了,更多相關(guān)Spring循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Kafka分區(qū)發(fā)送及消費(fèi)實(shí)戰(zhàn)

    Java Kafka分區(qū)發(fā)送及消費(fèi)實(shí)戰(zhàn)

    本文主要介紹了Kafka分區(qū)發(fā)送及消費(fèi)實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Spring中的Filter過(guò)濾器詳解

    Spring中的Filter過(guò)濾器詳解

    這篇文章主要介紹了Spring中的Filter過(guò)濾器詳解,Filter 程序是一個(gè)實(shí)現(xiàn)了特殊接口的 Java 類,與 Servlet 類似,也是由 Servlet 容器進(jìn)行調(diào)用和執(zhí)行的,需要的朋友可以參考下
    2023-08-08
  • IntelliJ IDEA2019 安裝lombok的實(shí)現(xiàn)

    IntelliJ IDEA2019 安裝lombok的實(shí)現(xiàn)

    這篇文章主要介紹了IntelliJ IDEA2019 安裝lombok的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • java獲取當(dāng)前時(shí)間的四種方法代碼實(shí)例

    java獲取當(dāng)前時(shí)間的四種方法代碼實(shí)例

    這篇文章主要介紹了java獲取當(dāng)前時(shí)間的四種方法代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • 模仿百度紅包福袋界面實(shí)例代碼

    模仿百度紅包福袋界面實(shí)例代碼

    新年到新年到,紅包搶不停。在我搶紅包的時(shí)候意外的發(fā)現(xiàn)了百度的福袋界面挺不錯(cuò)的,于是抽時(shí)間專門寫篇文章來(lái)完成百度紅包界面吧
    2016-02-02
  • 什么是Spring Boot

    什么是Spring Boot

    Spring是一個(gè)非常受歡迎的Java框架,它用于構(gòu)建web和企業(yè)應(yīng)用。本文介紹將各種Spring的配置方式,幫助您了解配置Spring應(yīng)用的復(fù)雜性
    2017-08-08
  • 使用JDBC4.0操作XML類型的字段(保存獲取xml數(shù)據(jù))的方法

    使用JDBC4.0操作XML類型的字段(保存獲取xml數(shù)據(jù))的方法

    jdbc4.0最重要的特征是支持xml數(shù)據(jù)類型,接下來(lái)通過(guò)本文重點(diǎn)給大家介紹如何使用jdbc4.0操作xml類型的字段,對(duì)jdbc4.0 xml相關(guān)知識(shí)感興趣的朋友一起看下吧
    2016-08-08
  • Java的接口調(diào)用時(shí)的權(quán)限驗(yàn)證功能的實(shí)現(xiàn)

    Java的接口調(diào)用時(shí)的權(quán)限驗(yàn)證功能的實(shí)現(xiàn)

    這篇文章主要介紹了Java的接口調(diào)用時(shí)的權(quán)限驗(yàn)證功能的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • MyBatisPlus代碼生成器的原理及實(shí)現(xiàn)詳解

    MyBatisPlus代碼生成器的原理及實(shí)現(xiàn)詳解

    這篇文章主要為大家詳細(xì)介紹了MyBatisPlus中代碼生成器的原理及實(shí)現(xiàn),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)MyBatisPlus有一定幫助,需要的可以參考一下
    2022-08-08
  • 一文讀懂Spring Cloud-Hystrix

    一文讀懂Spring Cloud-Hystrix

    這篇文章主要介紹了通過(guò)一文讀懂Spring Cloud-Hystrix的相關(guān)知識(shí),本文分步驟通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03

最新評(píng)論