下面這種狀況比較常見,A中注入了屬性B,B中注入了A屬性。java
@Component public class A { @Autowired private B b; //在A中注入B } @Component public class B { @Autowired private A a; //在B中注入A }
還有一種極限狀況,A中注入屬性A。spring
@Component public class A { @Autowired private A a; }
1、出現循環依賴的Bean必須是單例,原型不行。緩存
第一點很好理解,也很好驗證,由於原型的Bean,每次獲取的時候都會建立一個新的,那麼問題來了,假設在初始化A的時候,須要注入原型的B,接着新建一個A,又新建B……無窮盡。若是真是這樣,那還得了。所以,原型狀況下Spring沒法解決循環依賴,會報錯:併發
aused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
app
2、不全是構造器注入的方式。ide
依賴狀況 | 依賴注入方式 | 循環依賴是否被解決 |
---|---|---|
AB相互依賴(循環依賴) | 均採用setter方法注入 | 是 |
AB相互依賴(循環依賴) | 均採用構造器注入 | 否 |
AB相互依賴(循環依賴) | A中注入B的方式爲setter方法,B中注入A的方式爲構造器 | 是 |
AB相互依賴(循環依賴) | B中注入A的方式爲setter方法,A中注入B的方式爲構造器 | 否 |
在討論Spring如何解決循環依賴以前,咱們須要清除SpringBean的建立流程,以前的那篇文章討論了容器的啓動銷燬與對象完整的生命週期,這裏將其中涉及循環依賴的主要部分再作一個說明:函數
createBeanInstance
:實例化,其實也就是調用對象的構造方法或者工廠方法實例化對象populateBean
:填充屬性,這一步主要是對bean的依賴屬性進行注入(@Autowired
)initializeBean
:回調執行initMethod
、InitializingBean
等方法能夠想到,對於單例的bean,在createBeanInstance的時候,應該沒啥問題,循環依賴的問題應該發生在第二步屬性注入的時候,而這時後這個實例的狀態,正好處於:已經實例化,還未初始化的中間狀態。這一點很是關鍵!!!!post
在DefaultSingletonBeanRegistry
類中,維護了三個註釋以Cache of
開頭的Map,經過檢討能夠注意到,三級緩存與前兩級緩存不太同樣,Map中維護的值是ObjectFactory類型。學習
//單例緩存池 beanName - instance 一級緩存 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //bean的早期引用, bean name to bean instance 二級緩存 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); //單例工廠 beanName - ObjectFactory 三級緩存 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
singletonObjects
:一級緩存,一個單例bean【實例化+初始化】都完成以後,將會加入一級緩存,也就是咱們俗稱的單例池。earlySingletonObjects
:二級緩存,用於存放【實例化完成,還沒初始化】的實例,提早暴露,用於解決循環依賴問題。singletonFactories
:三級緩存,存放單例對象工廠ObjectFactory,與二級緩存不一樣的是,它能夠應對產生代理對象。@FunctionalInterface //函數式接口 public interface ObjectFactory<T> { T getObject() throws BeansException; }
還有幾個比較重要的集合:this
//bean被建立完成以後,註冊 private final Set<String> registeredSingletons = new LinkedHashSet<>(256); //正在建立過程當中的bean待的地兒,bean在開始建立的時候放入,知道建立完成將其移除 private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
AbstractBeanFactory.doGetBean中將會出現兩個重載的getSingleton方法:
protected <T> T doGetBean(...){ Object sharedInstance = getSingleton(beanName);// // // typeCheckOnly 爲 false,將當前 beanName 放入一個 alreadyCreated 的 Set 集合中。表示已經建立過一次 if (!typeCheckOnly) { markBeanAsCreated(beanName); } // 這個getSingleton方法很是關鍵。 //一、標註a正在建立中~ //二、調用singletonObject = singletonFactory.getObject();(實際上調用的是createBean()方法) 所以這一步最爲關鍵 //三、標註此時實例已經建立完成 //四、執行addSingleton()添加進一級緩存, //同時移除二級和三級緩存,還有註冊 sharedInstance = getSingleton(beanName, () -> { ... return createBean(beanName, mbd, args); }); }
protected Object getSingleton(String beanName, boolean allowEarlyReference)
咱們的流程進行到AbstractBeanFactory#doGetBean的時候,會執行Object sharedInstance = getSingleton(beanName);
,接着會執行getSingleton(beanName,true)
,一路跟進去,最終會進到DefaultSingletonBeanRegistry 的getSingleton方法,這個方法十分重要,咱們具體看一看:
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { //先從一級緩存中獲取,獲取到,就直接返回 Object singletonObject = this.singletonObjects.get(beanName //若是一級緩存獲取不到,且這個獲取的這個bean正在建立中,就從二級緩存中獲取 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { //從二級緩存中獲取 singletonObject = this.earlySingletonObjects.get(beanName); //仍是獲取不到,而且allowEarlyReference爲true if (singletonObject == null && allowEarlyReference) { //從三級緩存中獲取 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { //循環依賴第二次進入的時候,發現A 的三級緩存,因而能夠獲取到A 的實例, singletonObject = singletonFactory.getObject(); //獲取到以後將其置入二級緩存 this.earlySingletonObjects.put(beanName, singletonObject); //原先的那個就從三級緩存中移除 this.singletonFactories.remove(beanName); } } } } return singletonObject; }
另一個Singleton重載的方法:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { //將beanName放入到singletonsCurrentlyInCreation這個集合中,標誌着這個單例Bean正在建立 beforeSingletonCreation(beanName); boolean newSingleton = false; // 傳入的lambda在這裏會被執行,調用createBean方法建立一個Bean後返回 singletonObject = singletonFactory.getObject(); newSingleton = true; singletonObject = this.singletonObjects.get(beanName); } // 建立完成後將對應的beanName從singletonsCurrentlyInCreation移除 afterSingletonCreation(beanName); } if (newSingleton) { //加入一級緩存 addSingleton(beanName, singletonObject); } } return singletonObject; }
protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { //加入一級緩存 this.singletonObjects.put(beanName, singletonObject); //三級緩存移除 this.singletonFactories.remove(beanName); //二級緩存移除 this.earlySingletonObjects.remove(beanName); //註冊一下 this.registeredSingletons.add(beanName); } }
AbstractAutowireCapableBeanFactory#doCreateBean
在對象實例化完成,初始化以前進行:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. 實例化 BeanWrapper instanceWrapper = null; // 調用構造器或工廠方法 實例化 instanceWrapper = createBeanInstance(beanName, mbd, args); ////咱們一般說的bean實例,bean的原始對象,並無進行初始化的對象 A{ b:null} Object bean = instanceWrapper.getWrappedInstance(); //表示是否提早暴露原始對象的引用,對於單例的bean,通常來講爲true, 能夠經過allowCircularReferences關閉循環引用解決循環依賴問題 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName)); //是否容許單例提早暴露 if (earlySingletonExposure) { //調用這個方法,將一個ObjectFactory放進三級緩存,二級緩存會對應刪除 //getEarlyBeanReference方法: 一、若是有SmartInstantiationAwareBeanPostProcessor,調用他的getEarlyBeanReference方法,二、若是沒有,則不變仍是,exposedObject //這裏也是AOP的實現之處,AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));//在bean實例化後,屬性注入以前,Spring將bean包裝成一個工廠添加進三級緩存中 } //此時bean已經實例化完成, 開始準備初始化 // bean爲原始對象 Object exposedObject = bean; try { //負責屬性的裝配(如依賴注入),遇到循環依賴的狀況,會在內部getBean("b")->getSingleton(b) populateBean(beanName, mbd, instanceWrapper); //處理bean初始化完成後的各類回調這裏有可能返回一個代理對象 exposedObject = initializeBean(beanName, exposedObject, mbd); } //若是bean容許被早期暴露,進入代碼 if (earlySingletonExposure) { //第二參數爲false表示不會從三級緩存中在檢查,最多從二級緩存中找,其實二級緩存就夠了,其實以前getSingleton的時候,已經觸發了A 的ObjectFactory.getObject(),A實例已經放入二級緩存中 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { //若是沒有代理,進入這個分支 if (exposedObject == bean) { exposedObject = earlySingletonReference; / } }
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { //若是一級緩存中還沒有存在 if (!this.singletonObjects.containsKey(beanName)) { //添加到三級緩存中 this.singletonFactories.put(beanName, singletonFactory); //從二級緩存中移除 this.earlySingletonObjects.remove(beanName); //註冊一下 this.registeredSingletons.add(beanName); } } }
前面談到了這個方法,尚未細說:
//是否容許單例提早暴露 if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
它實際上就是調用了後置處理器的getEarlyBeanReference
,而真正實現了這個方法的後置處理器只有AbstractAutoProxyCreator
,與Aop相關,也就是說,在不考慮Aop的狀況下,這個方法壓根就和沒調用似的。這裏咱們也能更加明確,三級緩存出現很大程度上也是爲了更好處理代理對象。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { //調用後值處理器的getEarlyBeanReference exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject; }
咱們能夠跟進去看一看:
//AbstractAutoProxyCreator @Override public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); //若是須要的話,返回一個代理對象 return wrapIfNecessary(bean, beanName, cacheKey); }
那麼若是考慮可能會存在代理對象出現,這時三級緩存中存在的就是這個代理對象,而且以後經過getSingleton從三級緩存中取出,放入二級緩存中的也是這個對象。
本質其實就是 讓A注入B,B注入A ,B先注入的是一個還沒初始化就提早用的A 的引用。【這裏不考慮AOP】
以開頭的A,B爲例,假設他們都使用屬性字段注入:
A首先getBean,試圖獲取容器中單例A,第一次容器中還不存在,因而就須要開始建立A。
一頓操做,落點:A此時已經被實例化完成,可是尚未初始化,緊接着將A與一個ObjectFactory存入三級緩存 。若是A被AOP代理,經過這個工廠獲取到的就是A代理後的對象,若是沒有代理,工廠最後獲取到的就是A 的實例化對象。
初始化A,意爲A的屬性賦值,這時發現B須要注入,因而getBean,來一遍相同的步驟。
一頓操做,落點:B此時已經被實例化完成,可是尚未初始化,緊接着將B與一個ObjectFactory存入三級緩存 。
初始化B,發現須要注入A,因而getBean("a"),此時它在三級緩存中找到了A與ObjectFactory<?> singletonFactory
,經過singletonFactory.getObject();
獲得A的引用。並將其存入二級緩存,且從三級緩存移除 。
B注入從對象工廠得到的A的引用,此時B已經初始化完成【表明已經注入A成功,實際上是擁有了A的引用】,將B加入到一級緩存,並將B在二級緩存、三級緩存中的玩意清除,返回。
剛剛是A初始化到一半切出來開始實例化B的,那麼接下來也應該返回到A的初始化流程中去。
顯然B都已經初始化完畢了,A固然也順利地初始化成功了,一樣,也將A加入一級緩存中,並將A在二級緩存、三級緩存中清除。
至此,Spring解決循環依賴結束,A與B都已實例化+初始化完成,並存入一級緩存,且二級緩存、三級緩存中已經沒有了A和B。
固然了,這個過程實際上是在實例化A的時候,把B一併實例化了,因而在遍歷BeanNames實例化B的時候,就不須要進行這麼複雜的操做了,由於一級緩存中已經存在B了。
緣由在於,Spring解決循環依賴實際上是在Bean已經實例化但未初始化這個中間狀態的時候進行處理的,所以bean的實例化與初始化兩個操做必須分開,纔有機會存入三級緩存,提早暴露原始對象。
可是若是使用若是A先使用構造器,在注入的時候,他會去找B,B再注入A,可此時A並無暴露,也就失敗了。
但若是A先用setter注入,A會先暴露,再注入B,B再注入A的時候,就能夠經過三級緩存拿到A了。
顯然不能,Spring經過多個緩存達到存儲不一樣狀態的對象:
若是隻有一級緩存,併發狀況下,可能取到實例化但未初始化的對象。
三級緩存使用的是工廠,而不是引用,緣由在於:https://mp.weixin.qq.com/s/kS0K5P4FdF3v-fiIjGIvvQ
延遲隊實例化階段生成的對象的代理,只有真正發生循環依賴的時候,纔去提早生成代理對象,不然只會建立一個工廠並將其放入到三級緩存中,可是不會去經過這個工廠真正建立對象。
答:這個工廠的目的在於延遲對實例化階段生成的對象的代理,只有真正發生循環依賴的時候,纔去提早生成代理對象,不然只會建立一個工廠並將其放入到三級緩存中,可是不會去經過這個工廠去真正建立對象
咱們思考一種簡單的狀況,就以單首創建A爲例,假設AB之間如今沒有依賴關係,可是A被代理了,這個時候當A完成實例化後仍是會進入下面這段代碼:
// A是單例的,mbd.isSingleton()條件知足 // allowCircularReferences:這個變量表明是否容許循環依賴,默認是開啓的,條件也知足 // isSingletonCurrentlyInCreation:正在在建立A,也知足 // 因此earlySingletonExposure=true boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); // 仍是會進入到這段代碼中 if (earlySingletonExposure) { // 仍是會經過三級緩存提早暴露一個工廠對象 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }看到了吧,即便沒有循環依賴,也會將其添加到三級緩存中,並且是不得不添加到三級緩存中,由於到目前爲止Spring也不能肯定這個Bean有沒有跟別的Bean出現循環依賴。
假設咱們在這裏直接使用二級緩存的話,那麼意味着全部的Bean在這一步都要完成
AOP
代理。這樣作有必要嗎?不只沒有必要,並且違背了Spring在結合
AOP
跟Bean的生命週期的設計!Spring結合AOP
跟Bean的生命週期自己就是經過AnnotationAwareAspectJAutoProxyCreator
這個後置處理器來完成的,在這個後置處理的postProcessAfterInitialization
方法中對初始化後的Bean完成AOP
代理。若是出現了循環依賴,那沒有辦法,只有給Bean先建立代理,可是沒有出現循環依賴的狀況下,設計之初就是讓Bean在生命週期的最後一步完成代理而不是在實例化後就立馬完成代理。
圖片來源:http://www.javashuo.com/article/p-smkruniq-nt.html
Spring經過三級緩存解決了循環依賴:
singletonObjects
:一級緩存,一個單例bean【實例化+初始化】都完成以後,將會加入一級緩存,也就是咱們俗稱的單例池。earlySingletonObjects
:二級緩存,用於存放【實例化完成,還沒初始化】的實例,提早暴露,用於解決循環依賴問題。singletonFactories
:三級緩存,存放單例對象工廠ObjectFactory,與二級緩存不一樣的是,它能夠應對產生代理對象。Spring不可以解決先用構造器注入狀況的循環依賴,緣由在於Spring解決循環依賴的關鍵在於bean實例實例化完成,初始化以前的狀態,將其加入三級緩存,提早暴露bean。
最後,循環依賴應當在編碼的時候就考慮去儘可能避免,若是避免不了,那就儘可能不要使用構造器注入,可使用字段注入。
有點暈了,原本想簡單地學習一下,沒想到一套接着一套,頭暈眼花,仍是代碼看的太少了,繼續努力。感受有點亂,若是有說的不對的地方,還望評論區指點一二!!抱拳!!!
參考資料: