在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#doGetBeanspa
在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源碼時,在源碼上加的註釋