spring源碼分析之如何解決循環依賴

spring-ioc中循環依賴的問題,也算是高頻的面試問題了,今天跟大家一起來總結一下spring-ioc中是如何解決循環依賴的,相信大家是可以從這篇文章中徹底理解spring容器如何幫我們解決循環依賴,爲了更好的理解spring-ioc如何解決循環依賴,大家可以先簡單的瞭解spring-ioc中bean實例化的整個時序圖。

一、spring-ioc解決循環依賴的位置

在這裏插入圖片描述

紅色的標註框的地方,表示解決循環依賴的重點邏輯,後面會跟大家一起詳細閱讀,這裏大家可以先有個印象

二、spring-ioc解決循環依賴的動態示意圖

首先spring-ioc是無法解決構造函數中循環依賴的問題,這個後面會一起解釋。咱們先用一個簡單的例子和示意圖來描述spring-ioc解決循環依賴的思想,最後再去閱讀源碼,我相信大家能更容易理解

@Service
public class ACircleService {

    @Autowired
    private BCircleService bCircleService;
}
@Service
public class BCircleService {

    @Autowired
    private ACircleService aCircleService;
}

相信大家經常這麼使用,此時ACircleService(後面簡稱A)引用BCircleService(後面簡稱B)B中引用A就構成了一個循環依賴,下面我們用示意圖來描述它們的創建過程

1、首先大家理解下面幾個容器

(1)singletonsCurrentlyInCreation:表示當前正在創建的bean(僅僅存放名字beanName),如果沒創建完成,都會保存在這裏面

(2)singletonObjects:一級緩存,可以理解爲bean最終都是放在這裏面的,一個bean真正完成了就放到這裏面

(3)earlySingletonObjects:二級緩存,過渡使用

(4)singletonFactories:三級緩存,也是過渡使用(提前曝光的bean存放),它與二級緩存不同的是它放的是ObjectFactory,而不是最終的Bean,二級緩存中是放的三級緩存getObject的結果

介紹完上面的容器,我們接着看在創建A,B時它是如何從上述的容器中變化

2、變化過程圖

假如先創建A,此時四個容器中都是空的

(1)先依次從一級、二級、三級緩存中判斷是否有能拿到A,結果顯然是拿不到,四個容器都是空的,我就不畫了

(2)要開始創建A,此時需要往singletonsCurrentlyInCreation放入A,表示A正在實例化,此時四個容器的狀態如下

在這裏插入圖片描述
(3)接下來正式開始創建A到A創建完成(堆上面已經分配了空間,但是屬性還沒賦值),此時將A封裝成ObjectFactory對象(爲什麼要封裝,後面會講一下),大家可以認爲此時的A對象已經創建,但是屬性未賦值,我們暫時用下面命名AObjectFactory,但是AobjectFactory.getObject() == A(A的地址假設A@9527xxx),此時A是在堆上已經創建好了,但是它的屬性是null(bCircleService==null),我不知道這裏有沒有描述清楚,容器狀態如下:

在這裏插入圖片描述

(4)此時要給A的屬性賦值,這裏就是給bCircleService賦值,那麼就去創建B,創建B的過程和創建A的過程一樣的,先依次從一級緩存、二級緩存、三級緩存中獲取B,顯然獲取不到,那麼正式開始創建B,singletonsCurrentlyInCreation中加入B,表示當前也正在創建B,容器狀態如下:

在這裏插入圖片描述

(5)接下來同(3)類似,B創建完成,此時也只是在堆上創建好了對象,但是B中的屬性aCircleService還沒有賦值(aCircleService==null),此時將B封裝成BObjectFactory放到三級緩存,容器狀態如下:

在這裏插入圖片描述

(6)接下來是重點了,此時開始給B的屬性賦值了,這裏即給aCircleService 賦值,那麼它就要去創建A,並且把A的內存地址付給B中的aCircleService屬性,那麼創建A的過程和之前的一樣,先依次從一級、二級、三級緩存中拿A,此時是可以從三級緩存中拿到A的,那麼將拿到的A賦值給B的aCircleService屬性,此時aCircleService==A@9527xxx,此時B即將創建完成了,在全部創建完的前一步,將三級緩存中的B移到二級緩存(存放的是BObjectFactory.getObject()),因爲實例化B的全部步驟全部做完了,此時容器的狀態如下:

在這裏插入圖片描述

(7)此時B(假設地址是B@9527)已經全部實例化完成了,但是還有一些收尾的工作呀,就是需要從當前正在創建的容器(singletonsCurrentInCreation)中移除(表示B創建完成),並且同時將B移動到一級緩存singletonObjects中,此時的容器狀態

在這裏插入圖片描述

(8)上述B已經創建完成了,我們要記得創建B的時機是在A給bCircleService賦值的時候,所以我們的邏輯又到了給A的屬性賦值的時候了,此時我們知道B已經創建完成了,所以bCircleService==B@9527,此時A的實例化也快要結束了,A也要將三級緩存的內容移到二級緩存,類似過程(6),容器狀態如下:

在這裏插入圖片描述

(9)最後A也要做一些結束的動作,類似過程(7),到這裏A和B都已經全部實例化完成了

在這裏插入圖片描述

總結:上述看上去流程挺多的,其實主要的核心就是在A創建完成(對象已經在堆中分配),還沒有給屬性賦值(bCircleService==null)的過程中,將A封裝成ObjectFactory,放到三級緩存。然後在實例化B的過程中,給B的屬性aCircleService賦值時,依次 從容器中拿A,此時是可以從三級緩存中拿到,所以不會再去走創建A的過程了,相當於提前曝光了A

上面還留了兩個問題,會在下面的源碼分析中解釋

(1)爲什麼spring-ioc中無法解決構造函數中的循環依賴

(2)爲什麼需要使用三級緩存,而且裏面裝的是ObjectFactory

三、源碼分析

1、AbstractBeanFactory#doGetBean()

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

   final String beanName = transformedBeanName(name);
   Object bean;

   // Eagerly check singleton cache for manually registered singletons.
   //(1).先依次從一級、二級、三級緩存中看看能否取到
   Object sharedInstance = getSingleton(beanName);
    //(2).如果緩存中能取到,則不會走下面的創建
   if (sharedInstance != null && args == null) {
      if (logger.isTraceEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }
//(3).如果緩存中取不到,則走創建的邏輯
   else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
		//省略代碼...
         // Create bean instance.
         if (mbd.isSingleton()) {
             //(4).主要的創建邏輯
            sharedInstance = getSingleton(beanName, () -> {
               try {
                   //(5).java8函數式編程
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  // Explicitly remove instance from singleton cache: It might have been put there
                  // eagerly by the creation process, to allow for circular reference resolution.
                  // Also remove any beans that received a temporary reference to the bean.
                  destroySingleton(beanName);
                  throw ex;
               }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
		//省略代碼...
   }

   //省略代碼...
   return (T) bean;
}

上述代碼我省略了很多,主要保留了需要分析循環依賴的邏輯,上面已經加了註釋

首先從一級、二級、三級緩存中取

下面的if else針對是否能從緩存中取出結果

結合上述的圖,我們發現第一次創建A,顯然是走else的創建邏輯getSingleton

2、DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory)

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   synchronized (this.singletonObjects) {
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
         if (this.singletonsCurrentlyInDestruction) {
            throw new BeanCreationNotAllowedException(beanName,
                  "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                  "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
         }
         if (logger.isDebugEnabled()) {
            logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
         }
         //(1).開始創建之前調用方法
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
             //(2).真正的調用方法
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
         catch (IllegalStateException ex) {
            // Has the singleton object implicitly appeared in the meantime ->
            // if yes, proceed with it since the exception indicates that state.
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
               throw ex;
            }
         }
         catch (BeanCreationException ex) {
            if (recordSuppressedExceptions) {
               for (Exception suppressedException : this.suppressedExceptions) {
                  ex.addRelatedCause(suppressedException);
               }
            }
            throw ex;
         }
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }
            //(3).開始創建之後調用方法
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
             //(4).創建成功之後調用的方法
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

我在四個核心的方法上加了註釋

beforeSingletonCreation(beanName);

singletonFactory.getObject();

afterSingletonCreation(beanName);

addSingleton(beanName, singletonObject);

3、DefaultSingletonBeanRegistry#beforeSingletonCreation(beanName)

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

比較簡單,其實就是判斷當前正在創建的容器singletonsCurrentlyInCreation是否已經包含正在創建的類,如果包含,則拋異常,我們在構造做循環依賴就會在這裏拋異常,後面會具體分析爲什麼會在這裏拋異常

4、DefaultSingletonBeanRegistry#afterSingletonCreation(beanName);

這裏我們先沒有分析singletonFactory.getObject()創建的核心邏輯,因爲比較長一時半會兒分析不了,我們分析afterSingletonCreation是因爲它和上面的beforeSingletonCreation有關聯。這裏我們先假設singletonFactory.getObject()已經成功執行完了,我們看afterSingletonCreation()

protected void afterSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
      throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
   }
}

很簡單,就是將singletonsCurrentlyInCreation清除當前正在創建的bean,因爲此時我們已經創建完了,接下來再接着看addSingleton

5、DefaultSingletonBeanRegistry#addSingleton

protected void addSingleton(String beanName, Object singletonObject) {
   synchronized (this.singletonObjects) {
      this.singletonObjects.put(beanName, singletonObject);
      this.singletonFactories.remove(beanName);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
   }
}

很簡單,4、5的步驟對應我們的上述第二節示意圖(6)-(7)的過程。上面我們還沒有分析singletonFactory.getObject()創建bean的核心邏輯,只是假設它成功調用完成了,我們現在回過頭來分析

6、ObjectFactory#getObject()

這裏其實是調用的AbstractAutowireCapableBeanFactory#createBean(),因爲這裏是使用java8的lambda表達式,傳的是一個函數(參考1中代碼註釋(5)的那一段代碼)

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
//省略代碼....
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    //省略代碼...

}

省略了代碼,我們重點看doCreateBean,這個是核心

7、AbstractAutowireCapableBeanFactory#doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
       //(1).創建bean包裝類BealWrapper,這段代碼執行結束,說明bean已經構建完成,在堆上創建了實例
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
    //(2).拿到bean
   final Object bean = instanceWrapper.getWrappedInstance();

   //省略部分代碼...

   // Eagerly cache singletons to be able to resolve circular references
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
    //(3).這裏非常重要,沒有刪除原來的英文註釋,這裏就是提前曝光
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
      if (logger.isTraceEnabled()) {
         logger.trace("Eagerly caching bean '" + beanName +
               "' to allow for resolving potential circular references");
      }
       //(3-1)
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
       //(4).給屬性賦值
      populateBean(beanName, mbd, instanceWrapper);
       //(5).調用初始化方法
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   if (earlySingletonExposure) {
       //(6).bean在容器中移動
      Object earlySingletonReference = getSingleton(beanName, false);

  //省略部分代碼...

   return exposedObject;
}

這裏是解決循環依賴的核心—即提前曝光一個實例(該實例已經創建好,但是裏面的屬性還沒賦值,因爲賦值的邏輯要到代碼(4),而提前曝光的邏輯在(3))。拿上面的循環依賴A,B來說,代碼執行完(3)時,A對象已經在堆上分配,只是bCircleService == null而已。此時將A提前曝光,插入到三級緩存中;而實例化B的入口則在(4)中,給bCircleservice賦值,此時如果B沒有創建,就開始創建B。等B同樣執行完上述(3),則B也在堆上分配了,只是暫時B中的aCircleService==null,所以B執行(4)時,去創建A,此時創建A先依次從一級、二級、三級緩存中取A時是可以在三級緩存中取到,因此代碼不會執行1中的else邏輯,而是執行if。

我們現在來處理上述的第一個問題:爲什麼構造函數中的循環依賴不能解決?

我們還是拿A,B來舉例(假如A的構造函數中依賴B),如果在構造函數中循環依賴,則A不會上述代碼7中的(3)而是在(1)中就去獲取B(此時注意A沒有提前曝光,即一級、二級、三級緩存中都不存在),假如此時B沒有創建,則開始創建B。等B執行到(4)時,給B中的aCircleService賦值時,需要去創建A,先從一級、二級、三級緩存中去取A,取不到,則走代碼1中的else邏輯,else的邏輯最終會調用beforeSingletonCreation(beanName),因此就拋異常了。

我們處理第二個問題,爲什麼需要使用三級緩存singletonFactories,裏面裝的是ObjectFactory,而不是直接將ObjectFactory.getObject()獲取的結果放到二級緩存earlySingletonObjects呢?

這裏我們就需要看上述代碼7中的(3-1)addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));這裏是使用java8的函數式編程,如果不明白的,我用下面的匿名內部類來替換把

addSingletonFactory(beanName, new ObjectFactory<Object>(){

    @Override
    public Object getObject() throws BeansException {
        return getEarlyBeanReference();
    }
});

我們再看看getEarlyBeanReference()方法

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進行攔截,做一些處理,最終這個方法的調用時機,是在代碼2中的(4),此時bean的實例化的基本全部完成,所以這裏起到了延遲調用的作用。如果不延遲調用存在兩種情況

(1)不調用getEarlyBeanReference(),則有些實例創建沒有起到必要的攔截

(2)不延遲,那麼可能該實例僅僅是在堆上分配了,裏面屬性什麼都沒賦值,初始化方法也沒調用,可能我們用beanpostprocessor攔截也沒任何意義,達不到效果。

這裏大家可以思考一下這兩個問題,
1.當A構造函數中依賴B,而在B中依賴A不是構造函數依賴,會不會報錯
2.當A依賴B時不是構造函數依賴,而B依賴A時是構造函數依賴,會不會報錯
如果大家對這兩個問題都能回答正確,我相信是徹底理解了spring是如何解決循環依賴,如果回答錯誤,那麼還需要繼續看看

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章