1、循環引用:java
1. 定義: 循環依賴就是循環引用,就是兩個或多個Bean相互之間的持有對方,比方CircularityA引用CircularityB,CircularityB引用CircularityC,CircularityC引用CircularityA。造成一個環狀引用關係。緩存
2. 代碼演示樣例:oop
CircularityA
this
public class CircularityA { private CircularityB circularityB; public CircularityA() { } public CircularityA(CircularityB circularityB) { this.circularityB = circularityB; } public void setCircularityB(CircularityB circularityB) { this.circularityB = circularityB; } public void a() { circularityB.b(); } }
public class CircularityB { private CircularityC circularityC; public CircularityB() { } public CircularityB(CircularityC circularityC) { this.circularityC = circularityC; } public void setCircularityC(CircularityC circularityC) { this.circularityC = circularityC; } public void b() { circularityC.c(); } }
public class CircularityC { private CircularityA circularityA; public CircularityC() { } public CircularityC(CircularityA circularityA) { this.circularityA = circularityA; } public void setCircularityC(CircularityA circularityA) { this.circularityA = circularityA; } public void c() { circularityA.a(); } }
在Spring源代碼的AbstractAutowireCapableBeanFactory類中有例如如下代碼:spa
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { // Instantiate the bean. // 忽略此處代碼 // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. 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"); } addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } // 下面代碼忽略 }
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); if (exposedObject == null) { return exposedObject; } } } } return exposedObject; }
代碼中寫到了對單例Bean循環依賴的處理。 大體就是用遞歸的方法找出當前Bean的全部依賴Bean, 而後全部提早緩存起來。prototype
setter循環依賴(對於setter注入形成的依賴是經過Spring容器提早暴露剛完畢構造器注入但未完畢其它步驟(如setter注入)的Bean來完畢的,而且僅僅能解決單例做用域的Bean循環依賴)詳細處理過程例如如下:debug
(1) Spring容器建立單例「circularityA」 Bean。首先依據無參構造器建立「circularityA」 Bean, 並暴露一個exposedObject用於返回提早暴露的Bean。並將「circularityA」Bean放到Catch中。而後進行setter注入「circularityB」;code
(2) Spring容器建立單例「circularityB" Bean。首先依據無參構造器建立「circularityB" Bean,並暴露一個exposedObject用於返回提早暴露的Bean。並將「circularityB」 Bean放到Catch中,而後進行setter注入「circularityC」;中間件
(3) Spring容器建立單例「circularityC」 Bean,首先依據無參構造器建立「circularityC」 Bean,並暴露一個exposedObject用於返回暴露的Bean。並將「circularityC」 Bean放入Catch中, 而後進行setter注入「circularityA」。進行注入「circularityA」時由於步驟1提早暴露了exposedObject因此從以前的catch裏面拿Bean不用反覆建立。遞歸
(4) 最後在依賴注入「circularityB」和「circularityA」也是從catch裏面拿提早暴露的bean。 完畢setter注入。
但是對於「prototype」做用域Bean。Spring容器沒法完畢依賴注入,因爲「prototype」做用域的Bean,Spring容器不進行緩存,所以沒法提早暴露一個建立中的Bean。
另外一種Spring沒法解決的循環依賴方式----構造器循環依賴
如在建立CircularityA類時,構造器需要CircularityB類。那將去建立CircularityB,在建立CircularityB類時又發現需要CircularityC類,則又去建立CircularityC,終於在建立CircularityC時發現又需要CircularityA。 造成環狀依賴, 從而被Spring拋出。
Spring容器將每一個正在建立的Bean 標識符放在一個「當前建立Bean池」中,Bean標識符在建立過程當中將一直保持在這個池中,所以假設在建立Bean過程當中發現本身已經在「當前建立Bean池」裏時將拋出BeanCurrentlyInCreationException異常表示循環依賴。而對於建立完成的Bean將從「當前建立Bean池」中清除掉。
2、 循環調用
1. 定義: 循環調用事實上就是一個死循環(這個是沒法解決僅僅能提早避免), 終於形成StackOverflow。
2. 工做實例:
在作Hadoop的調度中間件的時候之前出現過這一個問題, 用到的解決方法事實上和Spring的解決循環依賴的思想很是類似。 Spring用的是緩存, 咱們當時用的是一個集合類。
Hadoop工做有一個常見流程: A --> B --> C
A、B、C是必須符合先後順序的。 但是業務系統的人可能在建立這樣的順序時建成A --> B --> C --> A造成一個環狀。 那麼這就是一種循環調用。
解決思想及時在創建關係時把A、B、C建立的時候就丟入集合類。 假設發現反覆那麼說明確定存在某種環在裏面。 而後作出對應處理。 就把循環調用提早阻止了。