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

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


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類中有例如如下代碼: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;
	}


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

代碼中寫到了對單例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建立的時候就丟入集合類。 假設發現反覆那麼說明確定存在某種環在裏面。 而後作出對應處理。 就把循環調用提早阻止了。

相關文章
相關標籤/搜索