一、循環引用:
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創建的時候就丟入集合類。 假設發現反復那么說明肯定存在某種環在里面。 然后做出對應處理。 就把循環調用提前阻止了。
