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的物件(TestA),(TestA還沒setter注入),此時可以作為物件填充依賴。
singletonObjects :儲存BeanName和bean的例項(此時物件已經完成了setter注入)
通過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)))); }
總結技術,也記錄生活 :
不是不想哭,
而是看到燈火闌珊的時候都沒有自己的家,
也覺得沒法在媽媽老之前給她想要的生活,
也是不想承認卻不得不努力的現實世界。