【spring源碼分析】IOC容器初始化(十二)

前言:在doCreateBean方法中還遺留一個問題沒有分析:循環依賴。循環依賴在Spring中是很是重要的一個知識點,所以單獨進行分析。web


什麼是循環依賴

循環依賴就是循環引用,兩個或兩個以上的bean互相引用對方,最終造成一個閉環。如A依賴B,B依賴C,C依賴A。以下圖所示:緩存

循環依賴其實就是一個死循環的過程,在初始化A的時候發現引用了B,則就會去初始化B,而後發現B又引用C,則又去初始化C,在初始化C的時候,再次發現C引用了A,則又去初始化A,這樣就處於死循環,除非有終結條件。websocket

Spring中循環依賴的場景有兩種:session

  • 構造器循環依賴。
  • setter循環依賴。

構造器循環依賴app

表示經過構造器注入構成的循環依賴,此依賴是沒法解決的,只能拋出BeanCurrentlyInCreationException異常。socket

setter循環依賴函數

表示經過setter注入方式構成的循環依賴,對於setter注入形成的循環依賴,Spring只解決單例模式下的循環依賴,對於其餘做用域的循環依賴,則拋出BeanCurrentlyInCreationException異常。爲何Spring只解決單例模式下的循環依賴呢,這裏首先了解下Spring的幾個做用域。this

Spring的scope做用域spa

  • singleton:默認的scope,每一個scope是singleton的bean都會被定義一個單例對象,該對象的生命週期與Spring IOC容器一致的(但在第一次注入時纔會建立)。
  • prototype:若是爲bean的做用域爲prototype,則每次注入時都會建立一個新的對象。
  • request:bean被定義爲在每一個HTTP請求 中建立一個單例對象,也就是說在單個請求中都會複用這個單例對象。
  • session:bean被定義爲在一個session的生命週期內建立一個單例對象。
  • application:bean被定義爲在ServletContext的生命週期中複用一個單例對象。
  • websocket:bean被定義爲在websocket的生命週期中複用一個單例對象。

從上面對Spring中scope做用域的介紹,也可大體瞭解爲何Spring中只解決單例模式下的循環依賴了,由於其餘做用域對象的生命週期並不與Spring IOC容器一致,而且最主要的一點是Spring並不會對除了單例模式的bean作緩存,所以Spring只能解決單例模式下的循環依賴。prototype

具體循環依賴解決流程

首先在看bean的加載的入口,在doGetBean方法中有以下代碼:

首先會根據beanName從單例bean緩存中獲取,若是不爲空,則直接返回,前面已經分析了該方法,這裏再次提出來。

 1 // DefaultListableBeanFactory
 2 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 3         // 從單例緩存中加載Bean
 4         Object singletonObject = this.singletonObjects.get(beanName);
 5         // 緩存中bean爲空,且當前bean正在建立
 6         if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
 7             // 作同步
 8             synchronized (this.singletonObjects) {
 9                 // 從earlySingletonObjects集合中獲取
10                 singletonObject = this.earlySingletonObjects.get(beanName);
11                 // earlySingletonObjects集合中沒有,其容許提早建立
12                 if (singletonObject == null && allowEarlyReference) {
13                     // 從singletonFactories中獲取對應的ObjectFactory
14                     ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
15                     if (singletonFactory != null) {
16                         // 獲取bean
17                         singletonObject = singletonFactory.getObject();
18                         // 將bean添加到earlySingletonObjects集合中
19                         this.earlySingletonObjects.put(beanName, singletonObject);
20                         // 從singletonFactories中移除對應的
21                         this.singletonFactories.remove(beanName);
22                     }
23                 }
24             }
25         }
26         return singletonObject;
27     }

分析:

這裏注意isSingletonCurrentlyInCreation函數,判斷當前bean是否正在被建立,這裏就是判斷beanName是否在singletonsCurrentlyInCreation集合中,那麼正在被建立的bean是合適添加進去的呢?仍是在doGetBean方法中

 1 // DefaultSingletonBeanRegistry
 2 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
 3         Assert.notNull(beanName, "Bean name must not be null");
 4         // 作同步
 5         synchronized (this.singletonObjects) {
 6             // 從緩存中檢查一遍
 7             // 由於singlton模式其實已經複用了建立的bean,因此該步驟必須檢查
 8             Object singletonObject = this.singletonObjects.get(beanName);
 9             // 爲空,開始進行加載
10             if (singletonObject == null) {
11                 if (this.singletonsCurrentlyInDestruction) {
12                     throw new BeanCreationNotAllowedException(beanName,
13                                                               "Singleton bean creation not allowed while singletons of this factory are in destruction " +
14                                                                       "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
15                 }
16                 if (logger.isDebugEnabled()) {
17                     logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
18                 }
19                 // 加載前置處理 其實就是打一個標記
20                 beforeSingletonCreation(beanName);
21                 // 首先將新的newSingleton設置爲false
22                 boolean newSingleton = false;
23                 boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
24                 if (recordSuppressedExceptions) {
25                     this.suppressedExceptions = new LinkedHashSet<>();
26                 }
27                 try {
28                     // 初始化bean
29                     // 該過程實際上是調用createBean()方法 這裏是一個回調方法
30                     singletonObject = singletonFactory.getObject();
31                     newSingleton = true;
32                 } catch (IllegalStateException ex) {
33                     // Has the singleton object implicitly appeared in the meantime ->
34                     // if yes, proceed with it since the exception indicates that state.
35                     singletonObject = this.singletonObjects.get(beanName);
36                     if (singletonObject == null) {
37                         throw ex;
38                     }
39                 } catch (BeanCreationException ex) {
40                     if (recordSuppressedExceptions) {
41                         for (Exception suppressedException : this.suppressedExceptions) {
42                             ex.addRelatedCause(suppressedException);
43                         }
44                     }
45                     throw ex;
46                 } finally {
47                     if (recordSuppressedExceptions) {
48                         this.suppressedExceptions = null;
49                     }
50                     // 一堆異常處理後,進行後置處理 移除標誌
51                     afterSingletonCreation(beanName);
52                 }
53                 // 新的bean 加入緩存中
54                 if (newSingleton) {
55                     addSingleton(beanName, singletonObject);
56                 }
57             }
58             return singletonObject;
59         }

分析:

注意第20行代碼beforeSingletonCreation函數。

1 // DefaultSingletonBeanRegistry
2 protected void beforeSingletonCreation(String beanName) {
3         // 這裏會添加到正在建立bean的集合中
4         // 注意第一個條件,若是存在,則爲false,直接短路
5         // 只有當第一個條件不存在[false]時,纔會去進行添加操做
6         if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
7             throw new BeanCurrentlyInCreationException(beanName);
8         }
9     }

分析:

這裏就會將正在建立的beanName添加到singletonsCurrentlyInCreation,若是出現構造函數的循環依賴bean注入,則會在此處拋出BeanCurrentlyCreationException異常,具體能夠跟着代碼調試一把,就能夠更清楚的知曉流程。

接下來繼續回到單例模式的循環依賴中:

在加載bean時,首先從單例緩存中獲取bean對象。

  • 首先從單例緩存中獲取bean對象,若是緩存中存在bean對象則直接返回(單例模式的bean在建立過程當中會進行緩存[singletonObjects])。
  • 若是緩存中bean對象爲空,且當前bean正在建立,則從earlySingletonObjects中獲取。
  • 若是earlySingletonObjects集合中不存在,且容許提早建立bean,則從singletonFactories中獲取單例工廠,若singleFactory不爲空,則經過getObject方法獲取bean,並將bean對象加入到earlySingletonObjects集合中,而後從singleFactory集合中移除對應的單例工廠對象。

注意這裏涉及到三個集合:

  • singletonObjects (一級)單例對象 Cache
  • earlySingletonObjects (二級)提早曝光的單例對象 Cache
  • singletonFactories (三級)單例對象工廠 Cache
 1 /**
 2      * Cache of singleton objects: bean name to bean instance.
 3      * 存放的是單例 bean 的映射。
 4      * <p>
 5      * 對應關係爲 bean name --> bean instance
 6      */
 7     private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 8 
 9     /**
10      * Cache of singleton factories: bean name to ObjectFactory.<br/>
11      * 存放的是 ObjectFactory,能夠理解爲建立單例 bean 的 factory 。
12      * <p>
13      * 對應關係是 bean name --> ObjectFactory
14      */
15     private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
16 
17     /**
18      * Cache of early singleton objects: bean name to bean instance.<br/>
19      * 存放的是早期的 bean,對應關係也是 bean name --> bean instance。
20      * <p>
21      * 它與 {@link #singletonFactories} 區別在於 earlySingletonObjects 中存放的 bean 不必定是完整。
22      * <p>
23      * 從 {@link #getSingleton(String)} 方法中,咱們能夠了解,bean 在建立過程當中就已經加入到 earlySingletonObjects 中了。
24      * 因此當在 bean 的建立過程當中,就能夠經過 getBean() 方法獲取。
25      * <p>
26      * 這個 Map 也是【循環依賴】的關鍵所在。
27      */
28     private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

這三個緩存集合就是解決Spring中循環依賴的所在,具體流程:

  • 首先從一級緩存singletonObjects中獲取,若是爲null,且當前bean正在被建立,則從二級緩存earlySingletonObjects中獲取,若是仍是爲null,且容許singletonFactories經過getObject獲取,則從三級緩存singletonFactories中獲取,若是獲得,則將其加入二級緩存earlySingletonObjects中,並從三級緩存singletonFactories中移除對應的工廠對象(由於單例模式的bean只會被建立一次),這樣三級緩存就升級到二級緩存了,因此二級緩存存在的意義就是緩存三級緩存中ObjectFactory#getObject的執行結果,提早曝光單例Bean對象。

若是從單例緩存中獲得bean對象,則會調用getObjectForBeanInstance方法進一步處理,由於從緩存中獲得的bean是最原始的bean,並不必定是最終所須要的bean對象。

上面分析了從緩存中獲取bean對象,可是緩存中的值是從什麼地方添加進來的呢?若是你調試過源碼會發現這樣一段代碼:

 1 // AbstractAutowireCapableBeanFactory
 2     boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
 3                 isSingletonCurrentlyInCreation(beanName));// 當前單例bean是否正在被建立
 4         if (earlySingletonExposure) {
 5             if (logger.isDebugEnabled()) {
 6                 logger.debug("Eagerly caching bean '" + beanName +
 7                                      "' to allow for resolving potential circular references");
 8             }
 9             // 提早將建立的bean實例加入到singletonFactories中
10             // 爲了後期避免循環依賴
11             addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
12         }

分析:

當一個bean知足三個條件,則會被加入到緩存中。三個條件以下:

  • 單例。
  • 運行提早暴露bean。
  • 當前bean正在被建立中。

DefaultSingletonBeanRegistry#addSingletonFactroy

 1     protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
 2         Assert.notNull(singletonFactory, "Singleton factory must not be null");
 3         synchronized (this.singletonObjects) {
 4             if (!this.singletonObjects.containsKey(beanName)) {
 5                 this.singletonFactories.put(beanName, singletonFactory);
 6                 this.earlySingletonObjects.remove(beanName);
 7                 this.registeredSingletons.add(beanName);
 8             }
 9         }
10     }

分析:

  • 若是單例緩存中不存在beanName,則將singletonFactory進行緩存[singletonFactories集合],注意這裏還有一個earlySingletonObjects.remove操做,該操做是刪除二級緩存(提早曝光的單例對象bean),由於二級緩存是從三級緩存轉換而來的,所以在對三級緩存[singletonFactories]進行緩存時,須要remove二級緩存。
  • 這段代碼發生在createBeanInstance方法以後,也就是說這個bean其實已經被建立出來了,但它仍是不是很完美(沒有進行屬性填充和初始化),可是對於其餘依賴它的對象而言已經足夠了(可根據對象引用定位到堆中對象),可以被認出來。因此Spring解決setter構造依賴的關鍵,就是對bean進行提早曝光。

上面介紹的了三級緩存[singletonFactories]與二級緩存[earlySingletonObjects]的出處,接下來看一級緩存的出處:

在DefaultSingletonBeanRegistry#getSingleton函數中:

 1 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
 2         Assert.notNull(beanName, "Bean name must not be null");
 3         // 作同步
 4         synchronized (this.singletonObjects) {
 5             // 從緩存中檢查一遍
 6             // 由於singlton模式其實已經複用了建立的bean,因此該步驟必須檢查
 7             Object singletonObject = this.singletonObjects.get(beanName);
 8             // 爲空,開始進行加載
 9             if (singletonObject == null) {
10                 if (this.singletonsCurrentlyInDestruction) {
11                     throw new BeanCreationNotAllowedException(beanName,
12                                                               "Singleton bean creation not allowed while singletons of this factory are in destruction " +
13                                                                       "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
14                 }
15                 if (logger.isDebugEnabled()) {
16                     logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
17                 }
18                 // 加載前置處理 其實就是打一個標記
19                 beforeSingletonCreation(beanName);
20                 // 首先將新的newSingleton設置爲false
21                 boolean newSingleton = false;
22                 boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
23                 if (recordSuppressedExceptions) {
24                     this.suppressedExceptions = new LinkedHashSet<>();
25                 }
26                 try {
27                     // 初始化bean
28                     // 該過程實際上是調用createBean()方法 這裏是一個回調方法
29                     singletonObject = singletonFactory.getObject();
30                     newSingleton = true;
31                 } catch (IllegalStateException ex) {
32                     // Has the singleton object implicitly appeared in the meantime ->
33                     // if yes, proceed with it since the exception indicates that state.
34                     singletonObject = this.singletonObjects.get(beanName);
35                     if (singletonObject == null) {
36                         throw ex;
37                     }
38                 } catch (BeanCreationException ex) {
39                     if (recordSuppressedExceptions) {
40                         for (Exception suppressedException : this.suppressedExceptions) {
41                             ex.addRelatedCause(suppressedException);
42                         }
43                     }
44                     throw ex;
45                 } finally {
46                     if (recordSuppressedExceptions) {
47                         this.suppressedExceptions = null;
48                     }
49                     // 一堆異常處理後,進行後置處理 移除標誌
50                     afterSingletonCreation(beanName);
51                 }
52                 // 新的bean 加入緩存中
53                 if (newSingleton) {
54                     addSingleton(beanName, singletonObject);
55                 }
56             }
57             return singletonObject;
58         }
59     }

分析:

這裏關注第54行處:addSingleton函數。

1 // DefaultSingletonBeanRegistry
2     protected void addSingleton(String beanName, Object singletonObject) {
3         synchronized (this.singletonObjects) {
4             this.singletonObjects.put(beanName, singletonObject);
5             this.singletonFactories.remove(beanName);
6             this.earlySingletonObjects.remove(beanName);
7             this.registeredSingletons.add(beanName);
8         }
9     }

分析:

這裏就是將bean加入一級緩存中[singletonObjects],同時remove二級緩存和三級緩存中值,由於bean已經被建立成功了,二級緩存與三級緩存也就不須要了。

總結

從上面分析過程能夠知道爲何Spring只解決單例模式下的循環依賴了吧?

  • Spring只對單例模式的bean進行了提早暴光[singletonFactories],這也是因爲其餘scope域bean的特性所致。

單例模式下循環依賴解決流程:

  • 首先A完成初始化第一步並將本身提早曝光出來(經過 ObjectFactory 將本身提早曝光),A在初始化的時候,發現本身依賴對象 B,此時就會去嘗試 get(B),這個時候發現 B 尚未被建立出來。
  • 而後B就走建立流程,在B初始化的時候,一樣發現本身依賴 C,C也沒有被建立出來。
  • 這個時候C又開始初始化進程,可是在初始化的過程當中發現本身依賴 A,因而嘗試 get(A),這個時候因爲A已經添加至緩存中(通常都是添加至三級緩存singletonFactories),經過 ObjectFactory 提早曝光,因此能夠經過ObjectFactory#getObject()方法來拿到 A 對象,C 拿到 A 對象後順利完成初始化,而後將本身添加到一級緩存中
  • 回到B,B 也能夠拿到 C 對象,完成初始化,A能夠順利拿到B完成初始化。到這裏整個鏈路就已經完成了初始化過程了。

若是你看過《Spring源碼深度解析》這本書,還能發現以下流程圖:

注:看圖說話,筆者最爲喜歡!其實去親身調試一下循環依賴的代碼,可能會有更加深入的認識。


by Shawn Chen,2019.04.29,下午。

相關文章
相關標籤/搜索