最新国产好看的视频,伊人天堂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)建過程和依賴注入的方式。依賴注入方式,我之前的博客有所分享,大家可以在看本篇文章之前進(jìn)行一下小小的回顧

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

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

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,無法解決循環(huá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)建中(通過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,無法解決循環(huán)依賴問題。

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

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

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

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

通過三級(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

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

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

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

相關(guān)文章

最新評(píng)論

清苑县| 文登市| 博客| 平远县| 青河县| 泰州市| 广水市| 大城县| 宁明县| 红河县| 威海市| 瑞金市| 雷波县| 榆中县| 泌阳县| 思茅市| 都安| 金平| 清河县| 弥渡县| 南充市| 临朐县| 尚志市| 康马县| 北票市| 永仁县| 三门县| 长宁区| 达拉特旗| 新绛县| 铅山县| 牟定县| 鄂温| 巴彦淖尔市| 卢龙县| 长丰县| 卓资县| 客服| 长武县| 融水| 蒲城县|