天天看點

【小明】談談你對Spring三級緩存和循環依賴的了解【建議收藏】

一、什麼是循環依賴?什麼是三級緩存?

【什麼是循環依賴】什麼是循環依賴很好了解,當我們代碼中出現,形如BeanA類中依賴注入BeanB類,BeanB類依賴注入A類時,在IOC過程中creaBean執行個體化A之後,發現并不能直接initbeanA對象,需要注入B對象,發現對象池裡還沒有B對象。通過建構函數建立B對象的執行個體化。又因B對象需要注入A對象,發現對象池裡還沒有A對象,就會套娃。

【三級緩存】三級緩存實際上就是三個Map對象,從存放對象的順序開始

        三級緩存singletonFactories存放ObjectFactory,傳入的是匿名内部類,ObjectFactory.getObject() 方法最終會調用getEarlyBeanReference()進行處理,傳回建立bean執行個體化的lambda表達式。

        二級緩存earlySingletonObjects存放bean,儲存半成品bean執行個體,當對象需要被AOP切面代時,儲存代理bean的執行個體beanProxy

        一級緩存(單例池)singletonObjects存放完整的bean執行個體

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);      
【小明】談談你對Spring三級緩存和循環依賴的了解【建議收藏】

二、三級緩存如何解決循環依賴?

【如何解決循環依賴】Spring解決循環依賴的核心思想在于提前曝光,首先建立執行個體化A,并在三級緩存singletonFactories中儲存執行個體化A的lambda表達式以便擷取A執行個體,當我沒有循環依賴和AOP時,這個三級緩存singletonFactories是沒用在後續用到的。

        但是當我A對象需要注入B對象,發現緩存裡還沒有B對象,建立B對象并又上述所說添加進三級緩存singletonFactories,B對象需要注入A對象,這時從半成品緩存裡取到半成品對象A,通過緩存的lambda表達式建立A執行個體對象,并放到二級緩存earlySingletonObjects中。

        此時B對象可以注入A對象執行個體和初始化自己,之後将完成品B對象放入完成品緩存singletonObjects。但是當有aop時,B對象還沒有把完成品B對象放入完成品緩存singletonObjects中,B對象初始化後需要進行代理對象的建立,此時需要從singletonFactories擷取bean執行個體對象,進行createProxy建立代理類操作,這是會把proxy&B放入二級緩存earlySingletonObjects中。這時候才會把完整的B對象放入完成品一級緩存也叫單例池singletonObjects中,傳回給A對象。

        A對象繼續注入其他屬性和初始化,之後将完成品A對象放入完成品緩存。

【小明】談談你對Spring三級緩存和循環依賴的了解【建議收藏】

三、使用二級緩存能不能解決循環依賴?

一定是不行,我們隻保留二級緩存有兩個可能性保留一二singletonObjects和earlySingletonObjects,或者一三singletonObjects和singletonFactories

【隻保留一二singletonObjects和earlySingletonObjects】

流程可以這樣走:執行個體化A ->将半成品的A放入earlySingletonObjects中 ->填充A的屬性時發現取不到B->執行個體化B->将半成品的B放入earlySingletonObjects中->從earlySingletonObjects中取出A填充B的屬性->将成品B放入singletonObjects,并從earlySingletonObjects中删除B->将B填充到A的屬性中->将成品A放入singletonObjects并删除earlySingletonObjects。

這樣的流程是線程安全的,不過如果A上加個切面(AOP),這種做法就沒法滿足需求了,因為earlySingletonObjects中存放的都是原始對象,而我們需要注入的其實是A的代理對象。

【隻保留一三singletonObjects和singletonFactories】

流程是這樣的:執行個體化A ->建立A的對象工廠并放入singletonFactories中 ->填充A的屬性時發現取不到B->執行個體化B->建立B的對象工廠并放入singletonFactories中->從singletonFactories中擷取A的對象工廠并擷取A填充到B中->将成品B放入singletonObjects,并從singletonFactories中删除B的對象工廠->将B填充到A的屬性中->将成品A放入singletonObjects并删除A的對象工廠。

同樣,這樣的流程也适用于普通的IOC已經有并發的場景,但如果A上加個切面(AOP)的話,這種情況也無法滿足需求。

因為拿到ObjectFactory對象後,調用ObjectFactory.getObject()方法最終會調用getEarlyBeanReference()方法,getEarlyBeanReference這個方法每次從三級緩存中拿到singleFactory對象,執行getObject()方法又會産生新的代理對象

@Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName); // 先從一級緩存拿
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName); // 拿二級緩存
                if (singletonObject == null && allowEarlyReference) {
                    // 拿三級緩存
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 
                    if (singletonFactory != null) {
                        // 最終會調用傳入的匿名内部類getEarlyBeanReference()方法,這裡面沒調用一次會生成一個新的代理對象
                        singletonObject = singletonFactory.getObject(); 
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }
    
    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;
    }