Spring Bean 循環依賴解決簡單分析

Spring Bean 循環依賴解決簡單分析

本文Spring版本:

5.0.5.RELEASE

什麼是循環依賴:

     循環依賴其實就是循環引用,也就是兩個或則兩個以上的bean互相持有對方,最終形成閉環。比如A 依賴 B,B 又依賴 A;或者A依賴於B,B依賴於C,C又依賴於A。

Spring 循環依賴的處理方式:

①構造器的循環依賴:這種依賴spring是處理不了的,直 接拋出BeanCurrentlylnCreationException異常。

②單例模式下的setter循環依賴:通過“三級緩存”處理循環依賴。

③非單例循環依賴:無法處理。

 

Spring 如何解決單例Bean的循環依賴問題?

利用三級緩存(DefaultSingletonBeanRegistry中定義)。

singletonObjects

用於存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用
earlySingletonObjects 存放原始的 bean 對象(尚未填充屬性)
singletonFactories 存放 bean對應的工廠對象(ObjectFactory)

 

解決循環依賴加載bean的大致流程:

 

判斷緩存中是否有X:

//DefaultSingletonBeanRegistry#getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //singletonObjects 第一級緩存,BeanFactory的單例全存在singletonObjects中
    //保存的是已經初始化完全的單例
    Object singletonObject = this.singletonObjects.get(beanName);
            //isSingletonCurrentlyInCreation=true,代表beanName所代表的bean循環依賴了
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //第二級緩存,保存的是未初始化完全的單例(只是實例化)
            singletonObject = this.earlySingletonObjects.get(beanName);
            //allowEarlyReference在當前場景下,默認爲true
            if (singletonObject == null && allowEarlyReference) {
                //第三級緩存,不是真的緩存,緩存的是生成二級緩存的工廠方法
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //通過緩存singletonFactories中的對象工廠獲取bean的對象
                    //如果有AOP,獲取的會是代理對象
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

實例化bean之後,會把獲取當前bean的方式放入到singletonFactories緩存:

注意:Map<String, ObjectFactory<?>> singletonFactories

該級緩存存放的並不是實例化的bean,而是一個ObjectFactory對象,它可以構造出對應的bean

//AbstractAutowireCapableBeanFactory#doCreateBean
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, new ObjectFactory<Object>() {
        @Override
        public Object getObject() throws BeansException {
            return getEarlyBeanReference(beanName, mbd, bean);
        }
    });
}

當bean實例化完成之後,會被放到一級緩存singletonObjects中,用於被其他bean獲取:

//DefaultSingletonBeanRegistry#getSingleton#addSingleton
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}

爲什麼是三級緩存?

      好像使用 earlySingletonObjects 和 singletonObjects 兩級緩存,一個存放早期對象,一個存放初始化完成後的對象,也能實現同樣的功能,singletonFactories 好像顯得有些多此一舉。其實不是的,對於普通對象,確實只要返回剛創建完的早期對象就好了,但對於內部有被 AOP 增強的方法的對象,需要返回的是代理對象。我們可以看一下 ObjectFactory 匿名內部類裏面調用的 getEarlyBeanReference 方法:

//AbstractAutowireCapableBeanFactory
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            // SmartInstantiationAwareBeanPostProcessor 這個後置處理器會在返回早期對象時 
            // 被調用,如果返回的對象需要加強,那這裏就會生成代理對象
            // 如果返回的對象需要AOP增強,那這裏exposedObject 就會替換成代理對象的引用
         // 比如上圖中的bean X 如果被AOP增強,那麼bean Y 加載時獲取的bean X 其實是X的代理對象 
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

我們可以看到對於一般的對象,返回的就是傳入的早期對象,但是對於內部有被 AOP 增強的方法的對象,會使用後置處理器返回一個代理對象。

如果在解決循環依賴時生成了代理對象,那麼 AbstractAutoProxyCreator 會把原對象放入一個 Map 中,這個 Map 的作用是防止同類對象被重複動態代理:

//AbstractAutoProxyCreator
public Object getEarlyBeanReference(Object bean, String beanName) {
// 提前暴露代理對象的引用,是在postProcessAfterInitialization之前執行
    Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            this.earlyProxyReferences.add(cacheKey);
        }

        return this.wrapIfNecessary(bean, beanName, cacheKey);
}
//AbstractAutoProxyCreator
//有些BeanPostProcessor提供對Bean的Wrap的操作,但是生命週期位於在set操作之後,避免重複代理
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
}

earlySingletonObjects作用是可以提升多次循環依賴加載的效率(singletonFactory.getObject() 不用多次執行),比如 X 依賴 Y, Y 依賴 X, Z 也依賴 X。Y 注入 X 時 earlySingletonObjects 已經通過 singletonFactory.getObject() 得到了原始X,這樣加載Y時就不用再執行 singletonFactory.getObject() 構造 X 實例了,直接從earlySingletonObjects獲取即可。

 

發佈了41 篇原創文章 · 獲贊 105 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章