1. 程式人生 > 程式設計 >SpringIOC容器解決迴圈依賴原始碼分析

SpringIOC容器解決迴圈依賴原始碼分析

spring的ioc來解決迴圈依賴的問題,從AbstractBeanFactory#getBean方法來進行原始碼分析,spring的解決迴圈依賴的原理,可以從三個本地快取Map集合進行調解,本篇文章打算從迴圈依賴的定義來解釋,在結合原始碼進行分析。

什麼是迴圈依賴

最簡單的例子,spring的迴圈依賴,就是類A依賴類B,類B依賴類A,他們之間就行成了迴圈依賴。這是最簡答例子,依據此方案就可以依賴3個類相互依賴可能導致死迴圈的狀況。看程式碼實現:

`

@Servcie
Class A {
    @Autowired
    private B b;
}

@Service
Class B {
    @Autowired
    private A a;
}
複製程式碼

IOC容器去載入bean的時候,按照順序去載入,會優先載入beanA然後會注入beanB,發現beanB沒被例項,接下來會載入beanB然後注入beanA,這時候發現beanA沒被例項。導致整個過程形成死迴圈,程式會一直載入,最終導致記憶體溢位。spring解決方案,採用三級快取的實現方案,在容器載入beanB的時候,發現beanB依賴beanA,容器會在載入beanA放入到早期快取,並把這個早期beanA注入到beanB中,beanB完成例項。beanA也就可以完成例項化。

三級快取介紹

`

/** Cache of singleton objects: bean name to bean instance. */
//最終快取
private final Map<String,Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
//早期快取,用於解決迴圈依賴
private final Map<String,ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
//存放bean工廠物件,用於解決迴圈依賴
private final Map<String,Object> earlySingletonObjects = new HashMap<>(16);
複製程式碼

原始碼分析

我們直接從AbstractBeanFactory#doGetBean方法開始分析。 `

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;

	// Eagerly check singleton cache for manually registered singletons.
	//對bean進行check,如果bean已經被例項就直接返回bean
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		if (logger.isTraceEnabled()) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
						"' that is not fully initialized yet - a consequence of a circular reference");
			}
			else {
				logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
			}
		}
		bean = getObjectForBeanInstance(sharedInstance,name,beanName,null);
	}

	else {
	 //。。。中間省略細節

	// Create bean instance.
	//根據bean的作用域去分析
	if (mbd.isSingleton()) {
		sharedInstance = getSingleton(beanName,() -> {
			try {
			    //這是建立bean的方法
				return createBean(beanName,mbd,args);
			}
			catch (BeansException ex) {
				// Explicitly remove instance from singleton cache: It might have been put there
				// eagerly by the creation process,to allow for circular reference resolution.
				// Also remove any beans that received a temporary reference to the bean.
				destroySingleton(beanName);
				throw ex;
			}
		});
		bean = getObjectForBeanInstance(sharedInstance,mbd);
	}

	else if (mbd.isPrototype()) {
		// It's a prototype -> create a new instance.
		Object prototypeInstance = null;
		try {
			beforePrototypeCreation(beanName);
			prototypeInstance = createBean(beanName,args);
		}
		finally {
			afterPrototypeCreation(beanName);
		}
		bean = getObjectForBeanInstance(prototypeInstance,mbd);
	}

	else {
		String scopeName = mbd.getScope();
		final Scope scope = this.scopes.get(scopeName);
		if (scope == null) {
			throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
		}
		try {
			Object scopedInstance = scope.get(beanName,() -> {
				beforePrototypeCreation(beanName);
				try {
					return createBean(beanName,args);
				}
				finally {
					afterPrototypeCreation(beanName);
				}
			});
			bean = getObjectForBeanInstance(scopedInstance,mbd);
		}
		catch (IllegalStateException ex) {
			throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +
					"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);
		}
	}
}
catch (BeansException ex) {
	cleanupAfterBeanCreationFailure(beanName);
	throw ex;
}
}
複製程式碼

` 上面是獲取bean的過程,先是從快取中獲取bean,如果沒有會去建立bean

`

protected Object getSingleton(String beanName,boolean allowEarlyReference) {
    //先從最終快取獲取例項
	Object singletonObject = this.singletonObjects.get(beanName);
	//判斷beanName對應的bean是否正在建立中
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
		    //從早期快取中獲取
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
			    //獲取相應的bean工廠
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					singletonObject = singletonFactory.getObject();
					//將剛例項的bean放入早期工廠中
					this.earlySingletonObjects.put(beanName,singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}
複製程式碼

上面的原始碼中,doGetBean 所呼叫的方法 getSingleton(String)是一個空殼方法,其 主要邏輯在 getSingleton(String,boolean) 中。該方法邏輯比較簡單,首先從 singletonObjects 快取中獲取 bean 例項。若未命中,再去 earlySingletonObjects 快取中獲取原始 bean 例項。如果仍未命中,則從 singletonFactory 快取中獲取 ObjectFactory 物件,然後再呼叫 getObject 方法獲取原始 bean 例項的應用, 也就是早期引用。獲取成功後,將該例項放入 earlySingletonObjects 快取中,並將 ObjectFactory 物件從 singletonFactories 移除。看完這個方法,我們再來看看 getSingleton(String,ObjectFactory) 方法,這個方法也是在 doGetBean 中被呼叫的。下面我們繼續往下跟蹤doCreateBean方法 `

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

	// Instantiate the bean.
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
	    //將bean例項化,並且包裹BeanWrapper物件中
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		instanceWrapper = createBeanInstance(beanName,args);
	}
	final Object bean = instanceWrapper.getWrappedInstance();
	Class<?> beanType = instanceWrapper.getWrappedClass();
	if (beanType != NullBean.class) {
		mbd.resolvedTargetType = beanType;
	}

	// Allow post-processors to modify the merged bean definition.
	synchronized (mbd.postProcessingLock) {
		if (!mbd.postProcessed) {
			try {
				applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName);
			}
			catch (Throwable ex) {
				throw new BeanCreationException(mbd.getResourceDescription(),"Post-processing of merged bean definition failed",ex);
			}
			mbd.postProcessed = true;
		}
	}

	// 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.isTraceEnabled()) {
			logger.trace("Eagerly caching bean '" + beanName +
					"' to allow for resolving potential circular references");
		}
		//將bean工廠物件加入到singletonFactories物件早期bean工廠
		addSingletonFactory(beanName,() -> getEarlyBeanReference(beanName,bean));
	}

	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
	        //注入屬性bean
		populateBean(beanName,instanceWrapper);
		//執行一些初始化引數例如BeanNameAware
		exposedObject = initializeBean(beanName,exposedObject,mbd);
	}
	catch (Throwable ex) {
		if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
			throw (BeanCreationException) ex;
		}
		else {
			throw new BeanCreationException(
					mbd.getResourceDescription(),"Initialization of bean failed",ex);
		}
	}
複製程式碼

` 在建立bean的過程分為三個部分,①、建立原始bean例項createBeanInstance,②、新增原始物件工廠物件到singletonFactories,③、注入屬性bean。下面這張圖是摘自別人部落格

總結

本篇文章是對整個ioc如何解決迴圈依賴的,其實本質就是ioc容器載入的核心部分,相應的bean生命週期的核心也能在本篇透析出來。以上就是ioc的講解,本人能力有限,大家相互進步喲。