一、循環引用:
1. 定義: 循環依賴就是循環引用,就是兩個或多個Bean相互之間的持有對方,比方CircularityA引用CircularityB,CircularityB引用CircularityC,CircularityC引用CircularityA。形成一個環狀引用關系。
2. 代碼演示樣例:
CircularityA
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(); } }
3. Spring源代碼:
在Spring源代碼的AbstractAutowireCapableBeanFactory類中有例如以下代碼:
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, 然后所有提前緩存起來。
setter循環依賴(對於setter注入造成的依賴是通過Spring容器提前暴露剛完畢構造器注入但未完畢其它步驟(如setter注入)的Bean來完畢的,並且僅僅能解決單例作用域的Bean循環依賴)詳細處理過程例如以下:
(1) Spring容器創建單例“circularityA” Bean。首先依據無參構造器創建“circularityA” Bean, 並暴露一個exposedObject用於返回提前暴露的Bean。並將“circularityA”Bean放到Catch中。然后進行setter注入“circularityB”;
(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池”中清除掉。
二、 循環調用
1. 定義: 循環調用事實上就是一個死循環(這個是無法解決僅僅能提前避免), 終於造成StackOverflow。
2. 工作實例:
在做Hadoop的調度中間件的時候以前出現過這一個問題, 用到的解決方法事實上和Spring的解決循環依賴的思想非常相似。 Spring用的是緩存, 我們當時用的是一個集合類。
Hadoop工作有一個常見流程: A --> B --> C
A、B、C是必須符合前后順序的。 可是業務系統的人可能在創建這樣的順序時建成A --> B --> C --> A形成一個環狀。 那么這就是一種循環調用。
解決思想及時在建立關系時把A、B、C創建的時候就丟入集合類。 假設發現反復那么說明肯定存在某種環在里面。 然后做出對應處理。 就把循環調用提前阻止了。