Spring源碼-循環依賴源碼解讀
筆者最近無論是看書還是從網上找資料,都沒發現對Spring源碼是怎么解決循環依賴這一問題的詳解,大家都是解釋了Spring解決循環依賴的想法(有的解釋也不准確,在《Spring源碼深度解析》作者也是看別人的博客說明了一下),沒有從源碼的角度分析是怎么解決循環依賴的,筆者就把自己看源碼的過程寫一下。
寫這一篇文章算是個引路的,Spring為了程序的健壯性做了大量分析校驗,調用的方法繁多復雜,我這篇文章為讀者清理出解決循環依賴的流程。
Spring中對象可以配置成單例模式也可配置為原型模式(原型模式很值得一看)。
Spring中可以通過構造函數注入、setter注入的方式來解決對象與對象間的依賴。
對象間的循環依賴只能配置單例、setter注入的方式來解決,其他方法就會報錯,下面我們通過源碼分析一下。
一、單例、setter注入解決循環依賴
假如有TestA、TestB、TestC三個對象,其中TestA依賴TestB,TestB依賴TestC,TestC依賴TestA。
下面具體通過代碼分析Spring是如何解決單例通過Setter注入的循環依賴。
在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry類中有幾個集合類型的成員變量,用來做緩存用的需要特別留意,源碼如下:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { ...... private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); private final Map<String, Object> earlySingletonObjects = new HashMap(16); private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16)); ...... }
上面的代碼中:
singletonsCurrentlyInCreation:保存對象的BeanName,在創建對象之前就會把對象的beanName保存起來。
singletonFactories:保存對象的BeanName和創建bean的工廠AbstractAutowireCapableBeanFactory(ObjectFactory),(對象的構造函數是在這一步完成的)
earlySingletonObjects:保存對象BeanName和對象的早期實例(ObjectFactory#getObject得到的對象)(此時對象還沒注入屬性),此時可以作為對象填充依賴。
singletonObjects:保存BeanName和bean的實例(此時對象已經完成了屬性注入)
通過Spring獲取testA對象:
通過Spring獲取testA對象 ApplicationContext factory=new ClassPathXmlApplicationContext("classpath:applicationContext-beans.xml"); TestA testA = (TestA) factory.getBean("testA"); System.out.println(testA);
factory.getBean最終調用的是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
下面看doGetBean源碼:
protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { ...... //重點1 Object sharedInstance = this.getSingleton(beanName); ...... if (mbd.isSingleton()) { //重點2 sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { //重點3 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); } }
在上面源碼中:
1、重點1:
//重點1 根據beanName試圖從緩存中獲取已經創建的對象,第一次進入是肯定返回null,這個函數放在后面解釋。
2、重點2:
在//重點2中才是真正創建TestA對象的方法,下面是//重點2 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton的源碼:
//重點2 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { ...... //重點2-1 beforeSingletonCreation(beanName); ...... try { //重點2-2 singletonObject = singletonFactory.getObject(); newSingleton = true; }catch (IllegalStateException ex) { ...... }catch (BeanCreationException ex) { ...... } finally { //重點2-3 afterSingletonCreation(beanName); } } }
在//重點2-1中beforeSingletonCreation方法中只做了一件事,就是保存beanName到singletonsCurrentlyInCreation(注意),這個時候testA就保存在singletonsCurrentlyInCreation里面了,源碼如下:
//重點2-1 protected void beforeSingletonCreation(String beanName) { //this.singletonsCurrentlyInCreation.add(beanName) if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
在 //重點2-2 會調用 //重點3 。
3、重點3:
在//重點3中org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean調用doCreateBean,doCreateBean方法源碼:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { ...... //重點3-1 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } //重點3-2 addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { //重點3-3 return getEarlyBeanReference(beanName, mbd, bean); } }); } ...... }
在//重點3-1判斷testA必須是單例,並存在在singletonsCurrentlyInCreation中,此時才會調用//重點3-2的addSingletonFactory方法,//重點3-2的addSingletonFactory方法源碼,
//重點3-2 protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { //重點3-2-1 this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
在上面的代碼中有一個singletonFactory的參數,這個參數是//重點3-3調用getEarlyBeanReference得到的,getEarlyBeanReference返回的就是ObjectFactory對象,是完成構造方法的。
在//重點3-2-1 向singletonFactories添加 ObjectFactory(注意),這個時候,testA和testA的ObjectFactory對象保存在singletonFactories,並移除earlySingletonObjects(現在earlySingletonObjects里面並沒有testA)。
執行完//重點3-2,發現testA依賴TestB對象,此時會遞歸調用getBean獲取TestB,testB執行步驟和上面testA一樣,然后testB依賴TestC,遞歸調用TestC,此時singletonFactories里面保存的數據如下:
testA -> ObjectFactory(TestA)
testB -> ObjectFactory(TestB)
testC -> ObjectFactory(TestC)
創建testC過程中執行完//重點3-2,發現依賴testA,此時會遞歸調用getBean獲取TestA,這時候執行到//重點1,下面開始解析//重點1 Object sharedInstance = getSingleton(beanName);方法。
4、重點1:
下面是調用//重點1 getSingleton(beanName),調用getSingleton(beanName, true)的源碼:
//重點1 protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { //重點1-1 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { //重點1-2 singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
在上面代碼中isSingletonCurrentlyInCreation就是判斷對象(testA)是否被保存過(在//重點2-1的時候testA就被保存了)
在//重點1-1中,從singletonFactories緩存中獲取到ObjectFactory(TestA),
在//重點1-2並通過模板模式獲取TestA對象,保存在earlySingletonObjects緩存中,並移除singletonFactories中的testA,
此時testC獲取到TestA的早期對象,可以注入TestA對象,自此TestC完成依賴注入,並把testC保存到singletonObjects中。
TestC創建完成,返回給testB,testB也算是完成了創建,然后返回給testA,自此循環依賴算是完成了。
總結:
單例的setter注入,會先把創建testA、testB、testC對象的ObjectFactory(對象工廠)保存在singletonFactories里面,然后testC依賴testA,那就從singletonFactories緩存中拿到testA的ObjectFactory,通過ObjectFactory的getObject獲取TestA的對象,並保存在earlySingletonObjects緩存中,清除singletonFactories緩存中的testA,此時testC就可以獲取earlySingletonObjects緩存中TestA的對象,完成注入TestA的過程。TestC對象創建完成就可以注入到TestB對象中,然后TestB注入到TestA中。
singletonsCurrentlyInCreation就是用來保存是否試圖創建某個對象的beanName,不管有沒有創建成功,為后來從singletonFactories緩存中或earlySingletonObjects緩存中取值做個標識。
二、單利、構造函數注入循環依賴
假如有TestA、TestB兩個對象,TestA依賴TestB,TestB依賴TestA;
構造函數注入和setter注入的不同在於,構造函數注入無法先調用構造函數實例化對象,當TestA依賴TestB,會先把testA保存到singletonsCurrentlyInCreation中,然后getBean("testB"),然后把testB保存到singletonsCurrentlyInCreation中,發現TestB依賴TestA,然后再getBean("testA"),此時執行下面的代碼(和模塊一,重點2是同一塊代碼):
//重點2 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton的源碼: public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { //重點2-0 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { ...... //重點2-1 beforeSingletonCreation(beanName); ...... try { //重點2-2 singletonObject = singletonFactory.getObject(); newSingleton = true; }catch (IllegalStateException ex) { ...... }catch (BeanCreationException ex) { ...... } finally { //重點2-3 afterSingletonCreation(beanName); } } }
在上面的代碼中 //重點2-0 Object singletonObject = this.singletonObjects.get(beanName); 此時singletonObject為空會執行beforeSingletonCreation方法,源碼如下:
//重點2-1 protected void beforeSingletonCreation(String beanName) { //this.singletonsCurrentlyInCreation.add(beanName) if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
結果發現singletonsCurrentlyInCreation已經存在testA,拋出BeanCurrentlyInCreationException。
setter注入為什么不會執行這一步呢,因為setter注入中會實例化TestA、TestB保存在緩存中,所以在執行 //重點2-0 Object singletonObject = this.singletonObjects.get(beanName); 時 singletonObject 不為空,並不會執行beforeSingletonCreation所以不會保存。
三、原型模式下循環依賴
假如有TestA、TestB兩個對象,TestA依賴TestB,TestB依賴TestA,當調用getBean("testA")時,會先把beanName(testA)保存到isPrototypeCurrentlyInCreation里面,發現TestA依賴TestB,就會去getBean("testB"),然后把beanName(testB)也保存到isPrototypeCurrentlyInCreation里面,此時TestB發現依賴TestA,去getBean("testA")時,發現isPrototypeCurrentlyInCreation已經存在testA,就會拋出BeanCurrentlyInCreationException異常,具體代碼如下
factory.getBean最終調用的是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean,
下面看doGetBean源碼:
protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { ...... if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } ...... } //isPrototypeCurrentlyInCreation源碼 protected boolean isPrototypeCurrentlyInCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); }