解析spring循環依賴

循環依賴

所謂循環依賴就是多個Bean之間依賴關係造成一個閉環,例如A->B->C->...->A 這種狀況,固然,最簡單的循環依賴就是2個Bean之間互相依賴:A->B(A依賴B), B->A(B依賴A) 。在Spring中,若是A->B,那麼在建立A的過程當中會去建立B,在建立B(或B的依賴)的過程當中又發現B->A,這個時候就出現了循環依賴的現象。spring

循環依賴的解決

spring中的循環依賴只有當緩存

  1. Bean是單例,
  2. 經過屬性注入的狀況

這兩個條件知足的狀況下是沒問題的。可是若是是經過構造器依賴,或者不是單例模式的狀況下循環依賴就會拋出異常BeanCurrentlyInCreationException。下面從代碼層面上解析一下爲何。app

Prototype的循環依賴問題

爲何最早介紹Prototype的循環依賴呢,由於能夠順便介紹在Spring中建立Bean的流程核心流程:在AbstractFoctory的doGetBean的方法。這個方法很長,這裏只寫出核心邏輯,並在註解上註明了我的理解:ide

protected <T> T doGetBean(
    	final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
    	throws BeansException {
    
    final String beanName = transformedBeanName(name);
    Object bean;
    
    //嘗試獲取單例對象,由於spring大部分的bean都是單例的,因此這裏先嚐試可否獲取。
    registered singletons.
    Object sharedInstance = getSingleton(beanName);
    //單例存在的狀況下,那麼beanName返回的確定是單例類,可是這裏還須要判斷是否是FactoryBean
    if (sharedInstance != null && args == null) {
        ...
        //FactoryBean應該返回getObject()對象
    	bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    
    else {
        //走到這裏,有可能beanName是單例模式,但以前並無實例化,或者是Prototype類型。
        //首先判斷不是循環依賴,這裏的循環依賴指的是Prototype類型
    	if (isPrototypeCurrentlyInCreation(beanName)) {
    		throw new BeanCurrentlyInCreationException(beanName);
    	}
    
    
    	try {
    		final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    		// 若是是單例,則建立單例模式
    		if (mbd.isSingleton()) {
    		    // !!!這裏是解決單例循環依賴的關鍵,後面再分析
    			sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
    				@Override
    				public Object getObject() throws BeansException {
    					try {
    						return createBean(beanName, mbd, args);
    					}
    					catch (BeansException ex) {
    						throw ex;
    					}
    				}
    			});
    			bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    		}
    
    		else if (mbd.isPrototype()) {
    			// 原型模式,則建立一個新對象.
    			Object prototypeInstance = null;
    			try {
    			    /*這裏是Prototype循環依賴的問題,會記錄在map中beanName,
    			    若是在解決當前Bean的依賴過程當中還依賴當前Bean,
    			    則說明了出現了循環依賴
    			    */
                            beforePrototypeCreation(beanName);
                            prototypeInstance = createBean(beanName, mbd, args);
    			}
    			finally {
    			    //對應beforePrototypeCreation(),從map中移除
                            afterPrototypeCreation(beanName);
    			}
    			bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    		}
    		...
    	}
    }
    
    ...
    return (T) bean;
    }

能夠看出,該流程中就考慮了Prototype的循環依賴的問題,只要在建立Prototype的Bean中出現循環依賴那麼就拋出異常。可是在singleton的狀況下,則經過另外的方式來解決。函數

Singleton的循環依賴之構造注入

在上面介紹中,出現了一個很關鍵的地方:ui

sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
    @Override
    public Object getObject() throws BeansException {
    	try {
            return createBean(beanName, mbd, args);
    	}
    	catch (BeansException ex) {
            throw ex;
    	}
    }
});

這個getSingleton涉及到了ObjectFactory這個接口類,這個接口的功能和FactoryBean相似,可是主要是用來解決循環依賴的。在初始化過程同決定返回的Singleton對象是。關於單例的對象的建立,又要介紹一下DefaultSingletonBeanRegistry這個類,這個類主要用來幫助建立單例模式,其中主要的屬性:this

/** 緩存建立的單例對象: bean名字 --> bean對象 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** 緩存單例的factory,就是ObjectFactory這個東西,: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

/** 也是緩存建立的單例對象,功能和singletonObjects不同,
在bean構形成功以後,屬性初始化以前會把對象放入到這裏,
主要是用於解決屬性注入的循環引用: bean name --> bean instance 
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** 記錄在建立單例對象中循環依賴的問題,還記得Prototype中又記錄建立過程當中依賴的map嗎?
在Prototype中只要出現了循環依賴就拋出異常,而在單例中會嘗試解決 */
private final Set<String> singletonsCurrentlyInCreation =
		Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

如今回過來看getSingleton(beanName, new ObjectFactory<Object>()這個方法的實現。prototype

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {

    synchronized (this.singletonObjects) {
        //嘗試在singletonObjects中獲取
    	Object singletonObject = this.singletonObjects.get(beanName);
    	if (singletonObject == null) {
    		//不存在則建立
    		//把當前beanName加入到singletonsCurrentlyInCreation中
    		beforeSingletonCreation(beanName);
    		try {
    		    
    			singletonObject = singletonFactory.getObject();
    		}
    		...
    		finally {
    			...
    			//從singletonsCurrentlyInCreation中刪除beanName
    			afterSingletonCreation(beanName);
    		}
    	}
    	return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
}

這段邏輯是否是和Prototype中解決循環相似,這裏其實就是調用了ObjectFactory的getObject()獲取對象,回過頭去看前面代碼,ObjectFactory的getObject()方法實際調用的是createBean(beanName, mbd, args)。說到createBean(beanName, mbd, args)又不得不說AbstractAutowireCapableBeanFactory這個類,主要功能就是完成依賴注入的Bean的建立,這個類的createBean方法代碼以下,注意註解說明:debug

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
    ...
	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    ...
}

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
			throws BeanCreationException {

	// 實例化bean
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
	    //若是沒實例化則建立新的BeanWrapper
	    //若是是經過構造器注入,這裏是一個關鍵點
	    /*
	    由於在A初始化的時候發現構造函數依賴B,就會去實例化B,
	    而後B也會運行到這段邏輯,構造函數中發現依賴A,
	    這個時候就會拋出循環依賴的異常
	    */
                instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	

   //若是當前是單例,而且allowCircularReferences爲true(默認就是true,除非咱們不但願Spring幫咱們解決)
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
			isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		/*
		!!!這裏很重要,把構形成功,但屬性還沒注入的
		的bean加到singletonFactory中,這樣再解決A的依賴
		過程當中若是依賴A,就把這個半成品返回回去。
		*/
		addSingletonFactory(beanName, new ObjectFactory<Object>() {
			@Override
			public Object getObject() throws BeansException {
				return getEarlyBeanReference(beanName, mbd, bean);
			}
		});
	}

	
	Object exposedObject = bean;
	try {
	    //自動注入屬性
		populateBean(beanName, mbd, instanceWrapper);
		if (exposedObject != null) {
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
	}
    ...

	return exposedObject;
}

註解已經註明了個人理解。就再也不贅述code

總結

上面代碼是我一邊debug一個寫下的,如今寫完了,根據本身的理解總結一下。

相關類說明

image

  • AbstractBeanFactory,這個類中包含了Bean建立的主要流程,在doGetBean這個方法中包含了對Prototype循環依賴處理。邏輯很簡單,出現了循環依賴則直接拋出異常
  • DefaultSingletonBeanRegister 用於管理Singleton的對象的建立,以及解決循環依賴的問題,其中解決循環依賴的關鍵屬性就是了earlySingletonObjects,他會在構造Singleton對象過程當中暫時緩存構形成功,但屬性還未注入的對象,這樣就能夠解決循環依賴的問題。
  • AbstractAutowireCapableBeanFactory,自動注入的相關邏輯,包自動注入的對象的建立、初始化和注入。但若是在調用構造函數中發現了循環依賴,則拋出異常
  • ObjectFactory,這個接口功能和FactoryBean相似,可是爲了解決循環依賴,他決定了在獲取的getSingleton()是一個完成品仍是一個半成品。

思考

若是A--構造依賴->B,B--屬性依賴-->A,例如:

@Component
public class BeanA {
    private BeanB beanB;

    @Autowired
    public BeanA(BeanB beanB) {
        this.beanB = beanB;
    }
}

@Component
public class BeanB {
    @Autowired
    private BeanA beanA;
}

這種狀況會異常嗎?提示:都有可能

相關文章
相關標籤/搜索