最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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)文章

最新評(píng)論

泾源县| 西乡县| 新疆| 龙游县| 利川市| 达拉特旗| 喜德县| 古交市| 兴隆县| 陕西省| 海原县| 察雅县| 洛扎县| 上思县| 循化| 棋牌| 威信县| 济南市| 建瓯市| 南靖县| 平潭县| 浮梁县| 如东县| 犍为县| 原平市| 宜黄县| 邛崃市| 游戏| 龙门县| 姜堰市| 弥勒县| 隆尧县| 昌宁县| 河东区| 乌拉特中旗| 临猗县| 桃源县| 城口县| 四平市| 启东市| 调兵山市|