Spring源代碼解析 ---- 循環依賴


一、循環引用


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();  
	}  
}


CircularityB

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();  
    }  
}  


CircularityC

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;
	}


這是Spring真正創建Bean的地方, 可是創建Bean就要考慮到處理循環引用又叫做循環依賴的問題。

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

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM