spring -- 循環依賴之SingletonObjects ,earlySingletonObjects和singletonfactories一級,二級和三級緩存 和 singletonsCurrentlyInCreation


前提:允許bean提前暴露(屬性還沒有賦值,有空的類對象) ,允許循環依賴 (循環依賴才往三級緩存中添加數據)

 

循環依賴的情況 一 (屬性中循環依賴)

例子:

循環依賴

@Component
public class CircleRefA {

    @Autowired
    private CircleRefB circleRefB;
}
@Component
public class CircleRefB {
    @Autowired
    private CircleRefA circleRefA;

    @Autowired
    private CircleRefC circleRefC;
}
@Component
public class CircleRefC {
    @Autowired
    private CircleRefA circleRefA;
}

假設spring中處理順序為 CircleRefA (beanName為circleRefA ) -》  CircleRefB (beanName為circleRefB )   -》  CircleRefC (beanName為circleRefC) 

先處理CircleRefA

先 getbean(circleRefA),這時一級緩存singletonObjects中沒有對應的類,且 singletonCurrentInCreation 中沒有 circleRefA , getbean(beanName) 返回 null ,調用getBean(circleRefA , ObjectFactory)  。此時先往 singletonCureentInCreation 中加入 circleRefA(表示正在創建 circleRefA 對應的實例) 。先創建一個屬性值為空的A ,稱為 早期的bean實例,調用addSingletonFactory ,往在三級緩存中添加circleRefA 對應的 ObjectFactory(此對象,就理解為時 A實例的包裝)再進行populateBean進行IOC填充circleRefA 。發現A中有CircleRefB的引用,進行CircleRefB實例化,進行 先 getbean(circleRefB)

 

在進行getbean(circleRefB),前面的操作一樣,后面再進行opulateBean進行IOC填充circleRefB 時,發現B中有CircleRefA的引用,進行getBean(circleRefA) (需要說明的時,屬性的編寫是認為的,此處可能先進行getBean(circleRefC),分析一樣)進行getBean(circleRefA)時,一級緩存singletonObjects中沒有對應的類,singetonCureentInCreation中有 circleRefA ;則查找二級緩存earlySingletonObjects ,沒有數據;再查找三級緩存,有數據 ,將數據填充到二級緩存earlyObjects , 刪除三級緩存,返回給B實例進行填充。發現有C類屬性,進行 getbean(circleRefC)

 

在進行getbean(circleRefC),前面操作同getBean(circleRefA)。后面再進行opulateBean進行IOC填充circleRefC 時,發現C中有CircleRefA的引用,進行getBean(circleRefA)  。此時getBean(circleRefA) ,是從二級緩存中拿到的數據 ,返回A對應的類。

 

完成 C 的實例化,一級緩存singletonObjects中添加C的實例,刪除 singletosCurrentlyInCreation中的circleRefC ,刪除三級級緩存中 circleRefC,刪除二級緩存 circleRefC。再遞歸完成 B , A的實例化(同C的實例化)。

 

 

說明:

singletonCureentInCreation 中存bean的名稱,表示實例正在創建

這里為什么有一個三級緩存singletonfactories,就是放置早期的bean實例

為什么有一個二級緩存earlySingletonObjects,其實也是放置早期的bean實例。多次拿  早期的bean實例 這一直接沖這里面拿。

直接放在三級緩存singletonfactories 中 ,不放在earlySingletonObjects中,功能上不是也可以嗎?是的

 

但是,其實獲取三級緩存的方式是

源碼 :  

//添加 三級緩存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            // 如果 一級緩存 中不存在
            if (!this.singletonObjects.containsKey(beanName)) {
                // 設置 三級緩存 ,緩存 的 是  singletonFactory 對象
                this.singletonFactories.put(beanName, singletonFactory);
                // 刪除 二級緩存
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

獲取三級緩存

ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
singletonObject = singletonFactory.getObject();
其中
@FunctionalInterface
public interface ObjectFactory<T> {

    /**
     * Return an instance (possibly shared or independent)
     * of the object managed by this factory.
     * @return the resulting instance
     * @throws BeansException in case of creation errors
     */
    T getObject() throws BeansException;

}

 

三級緩存是一個包裝bean的實例的對象,singletonFactory.getObject() 是 ,其實是調用的  getEarlyBeanReference(beanName, mbd, bean) ,

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        return exposedObject;
    }

這個地方就是預留了一個點位,進行操作 早期的類對象 ,將經過處理(有處理過程,可以放回原始的bean早期類對象,也可能不是,實現BeanPostProcessor接口,重寫getEarlyBeanReference就行,完全可以自定義修改) 。

 

就是我們說的三級緩存中,其實可以對 早期的類對象 進行自定已處理 。將處理完的對象 ,放在二級緩存中,若還有循環依賴的 處理 ,拿的是 經過處理的   早期的類對象 

 

循環依賴的情況 二 (多例循環依賴scope 為 prototype )

往三級緩存中添加數據源碼

        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

都不是單例,不會往三級緩存singletonfactories中添加數據。每次創建的都是新對象,沒必要緩存

此時根據 prototypesCurrentlyInCreation 判斷是不是再創建的 ,循環依賴的二次報錯。

創建是,會現在查

            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
再防止 this.prototypesCurrentlyInCreation.set(beanName);

 

循環依賴的情況 三 (構造函數的循環依賴)

@Component
public class CircleRefAA {

    @Autowired
    public CircleRefAA(CircleRefBB circleRefBB){
        System.out.println("---------CircleRefAA----------");
    }
}
@Component
public class CircleRefBB {

    @Lazy
    @Autowired
    public CircleRefBB(CircleRefAA circleRefAA){
        System.out.println("---------CircleRefBB----------");
    }
}

這個時候正在創建 早期的bean實例,三級緩存中沒有存數據 ,重復向 singletonsCurrentlyInCreation 中加數據,報錯。

        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

 

循環依賴的情況 四 (構造函數的循環依賴加@Lazy)

網上說構造函數的循環依賴加@Lazy可以解決

--未完待續

 

說的比較大白話,中間的一些話語 也不是專業術語,能力有限,單大體上就是 





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM