完全理解Spring如何解決循環依賴

Spring bean生命週期

能夠簡化爲如下5步。spring

一、構建BeanDefinition緩存

二、實例化 Instantiation多線程

三、屬性賦值 Populateapp

四、初始化 Initialization(BeanPostprocessor -> Aware,init)編輯器

五、銷燬 Destruction ide

Spring 三級緩存做用

一級緩存

/** Cache of singleton objects: bean name to bean instance. */
Map<String, Object> singletonObjects;

用來保存實例化、初始化都完成的bean對象。post

二級緩存

/** Cache of early singleton objects: bean name to bean instance. */
Map<String, Object> earlySingletonObjects ;

用來保存實例化完成,可是未初始化完成的對象(這個對象不必定是原始對象,也有多是通過AOP生成的代理對象)。this

三級緩存

/** Cache of singleton factories: bean name to ObjectFactory. */
Map<String, ObjectFactory<?>> singletonFactories;

用來保存一個對象工廠(ObjectFactory),提供一個匿名內部類,用於建立二級緩存中的對象。spa

三級緩存中提到的ObjectFactory即 () -> getEarlyBeanReference(beanName,mbd,bean),其中bean就是原始對象。.net

其中getEarlyBeanReference 方法是 SmartInstantiationAwareBeanPostProcessor接口中定義的,AbstractAutoProxyCreator(Spring AOP proxy creator)實現了該方法。

Spring三級緩存實現

獲取beanName:A

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(beanName:A)
  org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A)
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, allowEarlyReference:true)

分別按照一級緩存、二級緩存、三級緩存順序加載。若是存在循環依賴(好比beanName:B依賴beanName:A),並且三級緩存中存在beanName:A的引用,則從三級緩存中拿到beanName:A對應的提前曝光的對象(多是原始對象,也多是代理對象)並放入二級緩存。好比又有beanName:C依賴beanName:A,會直接從二級緩存中獲取到。

bean建立和初始化完成

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(beanName:A)
  org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, ObjectFactory:lamda表達,調用AbstractBeanFactory#createBean(beanName:A))
  org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton(beanName:A,singletonObject:A)

直接添加到一級緩存

bean建立完成以後

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(beanName:A, RootBeanDefinition:mbd, Object[]:args)
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(beanName:A, RootBeanDefinition:mbd, Object[]:args)
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory(beanName:A, () -> getEarlyBeanReference(beanName:A, mbd, bean:A))
    //放入三級緩存,beanName:A -> ObjectFactory( () -> getEarlyBeanReference(beanName:A, mbd, bean:A) )
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean  //屬性填充
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean   //初始化

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
   // Instantiate the bean.
   BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = instanceWrapper.getWrappedInstance();
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }
   // Initialize the bean instance.
   Object exposedObject = bean;
   populateBean(beanName, mbd, instanceWrapper);
   exposedObject = initializeBean(beanName, exposedObject, mbd);

   if (earlySingletonExposure) {
      Object earlySingletonReference = getSingleton(beanName, false);
      if (earlySingletonReference != null) {
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         }
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
         }
       }   
   }
}

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, allowEarlyReference:false) 

一個簡單的A、B互相依賴循環依賴場景

@Async註解循環依賴報錯

@Transactional使用的是自動代理建立器AbstractAutoProxyCreator,它實現了getEarlyBeanReference()方法從而很好的對循環依賴提供了支持。

@Async的代理建立使用的是AsyncAnnotationBeanPostProcessor單獨的後置處理器實現的,它只在一處postProcessAfterInitialization()實現了對代理對象的建立,所以若出現它被循環依賴了,就會報BeanCurrentlyInCreationException。

protected Object doCreateBean( ... ){
    ...
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    ...

    Object exposedObject = bean;

    /**
    *假如A實例有方法注有@Async註解,A實例依賴B實例,B實例依賴A實例。建立A實例時先走到populateBean方法,而後開始填充屬性B實例,B實例也會走到populateBean方法,而後從三級緩存中經過A流程中的getEarlyBeanReference()方法,從而拿到A的早期引用*。執行A的getEarlyBeanReference()方法的時候,會執行自動代理建立器,這裏最終獲得是多是原始A對象,也多是代理後的A對象(注意哦,A實例的@Async註解這裏尚未被處理呢)。exposedObject此時指向的是原始A實例。
    */
    populateBean(beanName, mbd, instanceWrapper);

     /**
     *標註有@Async的A實例的代理對象在此處會被生成, 參照類:AsyncAnnotationBeanPostProcessor。執行完以後,exposedObject指向的是個代理對象而非原始A實例了。
     */
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    
    ...
    // 這裏是報錯的重點。
    if (earlySingletonExposure) {
        /**
        *由於A被B循環依賴進去了,因此此時A是被放進了二級緩存的,因此此處earlySingletonReference指向的是經過建立A實例流程中的getEarlyBeanReference()返回的A實例(再強調用一下,多是原始對象,也多是代理對象)。
        *說到這裏,什麼狀況下earlySingletonReference==null?也就是getSingleton(beanName:A, false)==null,只有當A實例沒有牽涉到循環依賴的時候(即不存在A依賴B且B依賴A的場景;單獨存在B依賴A是沒有問題,A的三級緩存根本不會執行,因此二級緩存就不會有值,A建立並初始化完成以後直接放到了一級緩存)。
        */
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            //這裏exposedObject指向的是被@Aysnc代理過的對象,而bean是原始對象,因此此處不相等,走else邏輯。
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            /**
            *allowRawInjectionDespiteWrapping 標註是否容許此Bean的原始類型被注入到其它Bean裏面,即便本身最終會被包裝(代理)。
            *默認是false表示不容許,若是改成true表示容許,就不會報錯啦。這是咱們後面講的決方案的其中一個方案。
            *另外dependentBeanMap是記錄着每一個Bean它所依賴的Bean的Map。
            */
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                //由於A依賴於B,因此此處拿到了B實例的beanName
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);

                /**
                *B實例通過removeSingletonIfCreatedForTypeCheckOnly最終返返回false  由於alreadyCreated裏面已經有它了表示B已經徹底建立完成了。
                *既然B實例已經建立完成了,經過建立A實例流程中的getEarlyBeanReference()返回的A實例已經注入到了B實例中,此時B實例注入的和exposedObject指向的不是同一個A實例,那確定就有問題了。
                */
                for (String dependentBean : dependentBeans) {
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
    
                // 若存在這種真正的依賴,那就報錯了~~~  則個異常就是上面看到的異常信息
                if (!actualDependentBeans.isEmpty()) {
                    throw new BeanCurrentlyInCreationException(beanName,
                            "Bean with name '" + beanName + "' has been injected into other beans [" +
                            StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                            "] in its raw version as part of a circular reference, but has eventually been " +
                            "wrapped. This means that said other beans do not use the final version of the " +
                            "bean. This is often the result of over-eager type matching - consider using " +
                            "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                }
            }
        }
    }
    ...
}

具體緣由分析參考:http://www.javashuo.com/article/p-stnwfmxu-nt.html

總結

一、Spring 解決循環依賴有兩個前提條件:不全是構造器方式的循環依賴,必須是單例。

二、若是沒有出現循環依賴,第三級緩存(singletonFactories)將不會使用到,對象會按照Spring建立bean的生命週期流程,最後將bean直接放到第一級緩存(singletonObjects)中。

三、必定要三級緩存嘛,二級緩存不能解決循環依賴?不能,主要是爲了生成代理對象。

由於三級緩存中放的是生成具體對象的匿名內部類(ObjectFactory),它可能生成代理對象,也多是普通的實例對象。使用三級緩存主要是爲了保證無論何時使用的都是一個對象。假設只有二級緩存的狀況,往二級緩存中放的顯示一個普通的Bean對象,BeanPostProcessor去生成代理對象以後,覆蓋掉二級緩存中的普通Bean對象,沒法保證程多線程環境下獲取到bean對象一致性。

相關文章
相關標籤/搜索