Spring源碼分析---單例模式下循環依賴的解決

單例bean循環依賴解決

這版有錯誤 遞歸依賴加載並非在這個循環裏面,而是在填充屬性的時候進行加載的。spring

循環依賴定義緩存

在spring的啓動過程當中,有時候會出現bean之間的循環依賴,那什麼是循環依賴呢?就是A依賴B,B依賴A,在A未加載完成的狀況下,同時B開始了加載,這個時候又要注入A,這樣就會出現問題。bash

SPRING 對循環依賴的解決學習

spring對單例的bean有循環依賴的解決方案,可是目前對原型模式下的bean的循環依賴並無很好的解決方案。spring對單例bean 是採用提早在內存緩存中放置ObjectFactory來提早曝光bean,從而來解決循環依賴的問題的。ui

之前知道有這麼一回事,可是沒從源碼層面去剖析,因此似懂非懂,這樣不像是一位技術的master。 這裏我對spring源碼進行了簡化。代碼以下:this

AbstractBeanFactory doGetBean 方法spa

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		//從緩存中嘗試拿一遍bean 這個 getSingleton 方法裏面有 singletonFactories 內存map  用來存儲未加載好的bean 對應的ObjectFactory
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			// 處理FactoryBean
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
		else {
			try {
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {

					for (String dep : dependsOn) {
						try {
							//遞歸加載依賴的bean
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

				if (mbd.isSingleton()) {

					// 重載的getSingleton方法, 第二個參數是回調,在執行這個方法的時候 會執行createBean 方法,在這個方法裏面,ObjectFactory被提早曝光了。
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							destroySingleton(beanName);
							throw ex;
						}
					});

					// 處理FactoryBean
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}
		return (T) bean;
複製代碼

在以上這段代碼裏面,關鍵的註釋已經被給了出來。咱們看看這段代碼作了什麼事情。code

第一步: 先從緩存裏面拿單例bean,若是拿不出來,那麼再進行下面步驟的加載。若是拿的出來,那麼對它進行FactoryBean的處理。orm

第二步: 遞歸加載依賴的bean。 這個for循環被我簡化了不少,若是有興趣的朋友能夠看看原來的樣子,原來的樣子中有個拋異常的代碼仍是很是值得學習的。遞歸

第三步: 遞歸到底層,若是是單例bean,那就調用重載的getSingleton方法,這個方法的第二個參數是回調,在回調creatBean方法調用的doCreatBean中判斷這個bean是否仍是在建立狀態中,若是是,那就把ObjectFactory提早曝光。這個提早曝光的代碼層次有點深,能夠點進源碼看一下。


第一個getSingleton方法

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject =null;
		//這個單例是否是還在被建立,若是是,那麼就去取singletonFactories 中的 ObjectFactory
		if (isSingletonCurrentlyInCreation(beanName)) {
			
				if (allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
					}
				}
			}
		
		return singletonObject;
	}
複製代碼

看看以上代碼作了什麼:

這些代碼也被我簡化過,主要仍是從ObjectFactory池中拿ObjectFactory。


重載的getSingleton方法

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				
				//這個方法很重要,比較關鍵
				beforeSingletonCreation(beanName);
				
				try {
				
				    //執行回調
					singletonObject = singletonFactory.getObject();
					
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}
複製代碼

第二個重載的方法比較重要一樣也被我簡化了,在beforeSingletonCreation 方法裏面,咱們在singletonsCurrentlyInCreation 這個set裏面加入了beanName,標誌這個bean正在被建立。那麼執行到回調方法singletonFactory.getObject()的時候就開始提早曝光ObjectFactory了。咱們能夠來看看。

回調時調用的createBean方法的doCreateBean方法中有以下代碼:

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//提早曝光 ObjectFactory
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
複製代碼

咱們能夠看到earlySingletonExposure 布爾類型中有isSingletonCurrentlyInCreation方法,這個方法恰好也是根據上一步來的,看是否這個bean正在建立中。那麼若是是的,就提早曝光ObjectFactory。

因此,這樣spring就解決了單例模式下的循環依賴。有興趣的朋友能夠看看spring的源碼。

相關文章
相關標籤/搜索