spring源碼(三)--spring循環引用源碼學習

在spring中,是支持單實例bean的循環引用(循環依賴)的,循環依賴,簡單而言,就是A類中注入了B類,B類中注入了A類,首先貼出個人代碼示例java

 1 @Component
 2 public class AddressVO {
 3 
 4     @Autowired
 5     private UserVO userVO;
 6 }
 7 
 8 
 9 @Component
10 public class UserVO {
11 
12     @Autowired
13     private AddressVO addressVO;
14 
15     public void test(){
16         System.out.println(addressVO.getClass());
17     }
18 }
19 
20 @Configuration
21 @ComponentScan("com.springsource.study.reference")
22 public class AppConfig {
23 }

前提是,bean都是單實例的(singleton),咱們下面在學習源碼的時候,也默認當前bean是單實例的;git

1.首先說,循環依賴的處理是在refresh()方法中,finishBeanFactoryInitialization(beanFactory);這個方法中完成的;這個方法的做用是:在spring將全部的bean添加到beanDefinitionMap以後,實例化全部的bean;github

 先說明spring在解決循環依賴所用到的幾個比較重要的collectionspring

 

 1   //在實例化bean的時候,若是bean正在被建立,會在beforeSingletonCreation()方法中,將當前bean加入到singletonsCurrentlyInCreation 2   private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16));
 3  4 
 5   //從狹義上來理解,spring容器就是signletonObjects這個map;可是,詳細點來說,就是由beanDefinition+beanDefinitionMaps+beanFactory+beanFactoryPostprocessor+singletonObjects....等一些列組件的集合
 6   //這是spring單實例池(也就是咱們常說的spring容器)
 7   private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
 8 
 9   //咱們姑且稱爲spring的二級緩存,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));在該方法中,會將bean包裝爲ObjectFactory,存放到map中
10   private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
11 
12   //暫時稱爲三級緩存 三級緩存在循壞依賴中,用來防止重複建立
13   private final Map<String, Object> earlySingletonObjects = new HashMap(16);

上面這幾個集合,在源碼中會一一解析緩存

上面這裏少說了一個變量:allowCircularReferences;這個值默認是true;表示是否容許循環依賴app

 

2.咱們直接來講源碼吧:學習

  org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletonsthis

    org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)
      org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
spa

在doGetBean()方法中,咱們首先要說的是Object sharedInstance = getSingleton(beanName);這個方法:debug

 

 1 @Nullable
 2 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 3     Object singletonObject = this.singletonObjects.get(beanName);
 4     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
 5         synchronized (this.singletonObjects) {
 6             //這裏只須要從二級緩存中拿一次就行,若是沒有二級緩存,每次進來都須要從二級緩存get一次,影響效率
 7             singletonObject = this.earlySingletonObjects.get(beanName);
 8             if (singletonObject == null && allowEarlyReference) {
 9                 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
10                 if (singletonFactory != null) {
11                     singletonObject = singletonFactory.getObject();
12                     this.earlySingletonObjects.put(beanName, singletonObject);
13                     this.singletonFactories.remove(beanName);
14                 }
15             }
16         }
17     }
18     return singletonObject;
19 }

在第一次初始化bean,走到這個方法的時候,這裏返回的確定是null,由於在第一次建立的時候,單實例池、二級緩存、三級緩存中都是null;

這裏先這樣理解,在後面還會介紹這個方法;在這裏返回null以後,spring就會繼續往下進行建立初始化的流程;

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

在doGetBean方法中的,有這樣一段代碼

 1 if (mbd.isSingleton()) {
 2     sharedInstance = getSingleton(beanName, () -> {
 3         try {
 4             return createBean(beanName, mbd, args);
 5         }
 6         catch (BeansException ex) {
 7             // Explicitly remove instance from singleton cache: It might have been put there
 8             // eagerly by the creation process, to allow for circular reference resolution.
 9             // Also remove any beans that received a temporary reference to the bean.
10             destroySingleton(beanName);
11             throw ex;
12         }
13     });
14     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
15 }

這裏判斷到當前bean是單實例的時候,在getSingleton方法中有這麼一行代碼:beforeSingletonCreation(beanName);

這個方法,就是把當前bean添加到了singletonsCurrentlyInCreation這個set集合中;這個set的做用,在後面會用到;

 

接下來,會繼續執行初始化的代碼,中間的建立流程不作介紹了哈,直接說關鍵點

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

在這個方法中,有這麼幾行重要的代碼

 1 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
 2         isSingletonCurrentlyInCreation(beanName));
 3 if (earlySingletonExposure) {
 4     if (logger.isDebugEnabled()) {
 5         logger.debug("Eagerly caching bean '" + beanName +
 6                 "' to allow for resolving potential circular references");
 7     }
 8     //第四次調用後置處理器  用來解決循環依賴
 9     addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
10 }

這段代碼的意思是:

  若是當前bean是單實例的,而且容許循環依賴;isSingletonCurrentlyInCreation這個方法,就是判斷當前bean是否在singletonsCurrentlyInCreation這個set中,也就是說判斷當前bean是否正在被建立

  這三個條件都知足,因此會調用後置處理器

關鍵代碼是這個this.singletonFactories.put(beanName, singletonFactory); 將後置處理器返回的singletonFactory放到了咱們所說的二級緩存中

 

再繼續執行流程,初始化bean以後,會在populateBean(beanName, mbd, instanceWrapper);這個方法中,進行屬性的注入;

關鍵點就在這裏:

  咱們就以A類和B類來講了

    2.1 首先要明白的是,上面這一整個流程是第一個A這個bean初始化的過程;A類在進行屬性注入的時候,會發現依賴了B類,這時候,會調用 org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String);

    2.2 由於這時候,B類尚未初始化,因此,會進行B類的建立(B類的建立,就是再走一步咱們上面說的流程,),因爲流程是如出一轍的,添加到set集合中,調用後置處理器往二級緩存中放入singletonFactory,這些都同樣,當B類來到屬性注入的時候;會發現B類依賴了A類;也會去調用getBean來判斷是否有A類;關鍵點就是這一次調用getBean的時候

   

@Nullable
 2 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 3     Object singletonObject = this.singletonObjects.get(beanName);
 4     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
 5         synchronized (this.singletonObjects) {
 6        
 7             singletonObject = this.earlySingletonObjects.get(beanName);
 8             if (singletonObject == null && allowEarlyReference) {
 9                 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
10                 if (singletonFactory != null) {
11                     singletonObject = singletonFactory.getObject();
12                     this.earlySingletonObjects.put(beanName, singletonObject);
13                     this.singletonFactories.remove(beanName);
14                 }
15             }
16         }
17     }
18     return singletonObject;
19 }

這一次在調用getBean的時候,singletonObjects裏面,仍是獲取不到A類,可是:isSingletonCurrentlyInCreation(beanName)這個判斷條件會知足,由於A類在初始化的時候,放到了這個set集合中;

這個從earlySingletonObjects中獲取不到A類,因此,會執行ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);這一行代碼,返回singletonFactory中的object;

  2.3 在這一次getBean的時候,A類返回了,因此B類會完成初始化,而後再在A類中注入B類

 

3 到這裏,循環依賴就完成了,不知道有沒有描述清楚:

我再描述一下把:

   3.1 在A類第一次初始化的時候,會將A類放到表示正在建立的set集合中;將A類放到二級緩存的map中,而後在屬性注入的時候,發現A類依賴了B類,會調用getBean,這時候,B類返回的是null,會進行初始化

   3.2 B類初始化的流程和A類初始化流程是同樣的,將B類放到表示當前bean正在建立的set集合中,將B類放到二級緩存中,而後進行B類的屬性注入,在這是,會發現,B類依賴了A類,而後會調用getBean,這時候,雖然spring單實例池中,尚未A類,可是:二級緩存中有,因此就返回了二級緩存中的A類,讓B類完成了初始化流程,

   3.3 B類完成了初始化,那A類依賴的B也就注入了

 

最後再貼一張debug的流程圖

 

 

 

紅色的是A --> B --> A

綠色的是 屬性注入具體調用的方法

 

最後再附上我的在學習spring源碼時,在源碼上加的註釋

https://github.com/mapy95/spring-sourceCode

相關文章
相關標籤/搜索