溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Spring 源碼(八)循環(huán)依賴(lài)

發(fā)布時(shí)間:2020-10-19 21:33:41 來(lái)源:網(wǎng)絡(luò) 閱讀:169 作者:艾弗森哇 欄目:開(kāi)發(fā)技術(shù)

循環(huán)依賴(lài)是指兩個(gè)或者多個(gè)Bean之前相互持有對(duì)方。在Spring中循環(huán)依賴(lài)一般有三種方式:

  1. 構(gòu)造函數(shù)循環(huán)依賴(lài)

  2. setter方法循環(huán)依賴(lài)

  3. prototype 范圍的依賴(lài)處理

構(gòu)造函數(shù)循環(huán)依賴(lài)

在Spring中構(gòu)造函數(shù)循環(huán)依賴(lài)是無(wú)法解決的,因?yàn)闃?gòu)造函數(shù)依賴(lài)其實(shí)是方法間循環(huán)調(diào)用的一種,會(huì)發(fā)生死循環(huán)。但是在Spring中會(huì)直接拋出BeanCurrentlyInCreationException異常。源碼如下:

//?在緩存中獲取Bean,如果沒(méi)有就創(chuàng)建Beanpublic?Object?getSingleton(String?beanName,?ObjectFactory<?>?singletonFactory)?{
	Assert.notNull(beanName,?"'beanName'?must?not?be?null");	synchronized?(this.singletonObjects)?{		//?在緩存中獲取Bean
		Object?singletonObject?=?this.singletonObjects.get(beanName);		if?(singletonObject?==?null)?{			//?判斷容器是否正在銷(xiāo)毀單實(shí)例Bean
			if?(this.singletonsCurrentlyInDestruction)?{				throw?new?BeanCreationNotAllowedException(beanName,						"Singleton?bean?creation?not?allowed?while?singletons?of?this?factory?are?in?destruction?"?+						"(Do?not?request?a?bean?from?a?BeanFactory?in?a?destroy?method?implementation!)");
			}			//?將當(dāng)前需要?jiǎng)?chuàng)建的Bean標(biāo)示放到Set集合,如果失敗則拋出BeanCurrentlyInCreationException異常
			beforeSingletonCreation(beanName);			boolean?newSingleton?=?false;			boolean?recordSuppressedExceptions?=?(this.suppressedExceptions?==?null);			if?(recordSuppressedExceptions)?{				this.suppressedExceptions?=?new?LinkedHashSet<Exception>();
			}			try?{				//?創(chuàng)建Bean實(shí)例
				singletonObject?=?singletonFactory.getObject();
				newSingleton?=?true;
			}
			...			if?(newSingleton)?{				//?將Bean實(shí)例注冊(cè)到singletonObjects容器中
				addSingleton(beanName,?singletonObject);
			}
		}		return?(singletonObject?!=?NULL_OBJECT???singletonObject?:?null);
	}
}protected?void?beforeSingletonCreation(String?beanName)?{	//?將當(dāng)前需要?jiǎng)?chuàng)建的Bean標(biāo)示方法Set集合,如果失敗則拋出BeanCurrentlyInCreationException異常
	if?(!this.inCreationCheckExclusions.contains(beanName)?&&?!this.singletonsCurrentlyInCreation.add(beanName))?{		throw?new?BeanCurrentlyInCreationException(beanName);
	}
}

執(zhí)行過(guò)程:

  1. 從緩存中獲取Bean,如果沒(méi)有則走創(chuàng)建Bean流程

  2. 判斷容器是否正在銷(xiāo)毀單實(shí)例Bean,如果是則不創(chuàng)建Bean

  3. 將當(dāng)前需要?jiǎng)?chuàng)建的Bean標(biāo)示(name)放入Set集合中(當(dāng)前正在創(chuàng)建的Bean池),如果放入失敗則拋出BeanCurrentlyInCreationException異常

  4. 創(chuàng)建Bean實(shí)例

  5. 將Bean實(shí)例注冊(cè)到容器(放到緩存中)

解決構(gòu)造函數(shù)依賴(lài)主要是第3步實(shí)現(xiàn)的,Spring在容器創(chuàng)建的Bean的時(shí)候,會(huì)將Bean的標(biāo)示(name)放到一個(gè)Set集合里面(當(dāng)前正在創(chuàng)建的Bean池)。當(dāng)在創(chuàng)建Bean的過(guò)程中,發(fā)現(xiàn)自已經(jīng)在這個(gè)Set集合中時(shí),就直接會(huì)拋出BeanCurrentlyInCreationException異常,而不會(huì)發(fā)生死循環(huán)。

setter方法循環(huán)依賴(lài)

@Servicepublic?class?AService?{????@Autowired
????private?BService?bService;????@Autowired
????public?void?setbService(BService?bService)?{????????this.bService?=?bService;
????}
}

這兩種方式都算是setter方法依賴(lài)。我們創(chuàng)建單實(shí)例Bean的大致過(guò)程可以劃分成三個(gè)階段:

  1. 實(shí)例化?createBeanInstance(beanName, mbd, args);

  2. 設(shè)置屬性值?populateBean(beanName, mbd, instanceWrapper);

  3. 初始化?initializeBean(beanName, exposedObject, mbd);

對(duì)于Setter注入造成的循環(huán)依賴(lài),Spring容器是在創(chuàng)建Bean第一步實(shí)例化后,就將Bean的引用提前暴露出來(lái)。通過(guò)提前暴露出一個(gè)單例工廠方法,從而使得其他Bean可以引用到該Bean。

創(chuàng)建Bean時(shí)提前暴露剛完成第一步的Bean,源碼如下:

addSingletonFactory(beanName,?new?ObjectFactory<Object>()?{	@Override
	public?Object?getObject()?throws?BeansException?{		return?getEarlyBeanReference(beanName,?mbd,?bean);
	}
});//?將單例工廠放入緩存中protected?void?addSingletonFactory(String?beanName,?ObjectFactory<?>?singletonFactory)?{
	Assert.notNull(singletonFactory,?"Singleton?factory?must?not?be?null");	synchronized?(this.singletonObjects)?{		if?(!this.singletonObjects.containsKey(beanName))?{			this.singletonFactories.put(beanName,?singletonFactory);			this.earlySingletonObjects.remove(beanName);			this.registeredSingletons.add(beanName);
		}
	}
}

自動(dòng)裝配過(guò)程中獲取單實(shí)例Bean,源碼如下:

protected?Object?getSingleton(String?beanName,?boolean?allowEarlyReference)?{
	Object?singletonObject?=?this.singletonObjects.get(beanName);	if?(singletonObject?==?null?&&?isSingletonCurrentlyInCreation(beanName))?{		synchronized?(this.singletonObjects)?{
			singletonObject?=?this.earlySingletonObjects.get(beanName);			if?(singletonObject?==?null?&&?allowEarlyReference)?{
				ObjectFactory<?>?singletonFactory?=?this.singletonFactories.get(beanName);				if?(singletonFactory?!=?null)?{
					singletonObject?=?singletonFactory.getObject();					this.earlySingletonObjects.put(beanName,?singletonObject);					this.singletonFactories.remove(beanName);
				}
			}
		}
	}	return?(singletonObject?!=?NULL_OBJECT???singletonObject?:?null);
}

我們可以看到,在自動(dòng)裝配Bean的過(guò)程中,會(huì)去找三個(gè)緩存:

  1. singletonObjects:存放完成創(chuàng)建的Bean所有步驟的單實(shí)例Bean

  2. earlySingletonObjects:存放只完成了創(chuàng)建Bean的第一步,且是由單實(shí)例工廠創(chuàng)建的Bean

  3. singletonFactories:存放只完成了創(chuàng)建Bean的第一步后,提前暴露Bean的單實(shí)例工廠。http://www.chacha8.cn/detail/1132398214.html

這里為什么會(huì)用一個(gè)三級(jí)緩存呢,為啥不直接將只完成了創(chuàng)建Bean的第一步的Bean直接方到earlySingletonObjects緩存中呢?

我們跟入?getEarlyBeanReference(beanName, mbd, bean)我們可以發(fā)現(xiàn),單實(shí)例工廠方法返回Bean的時(shí)候還執(zhí)行了后置處理器的,在后置處理器中我們還可以對(duì)Bean進(jìn)行一些特殊處理。如果我們直接將剛完成實(shí)例化的Bean放入earlySingletonObjects緩存中,那么失去對(duì)Bean進(jìn)行特殊處理的機(jī)會(huì)。

源碼如下:

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?null;
				}
			}
		}
	}	return?exposedObject;
}

對(duì)于singleton?作用域 bean ,可以通過(guò)setAllowCircularReferences(false);來(lái)禁用循環(huán)引用。鄭州不育不孕醫(yī)院:http://www.zzchyy110.com/

prototype 范圍的依賴(lài)處理

對(duì)于prototype?作用域 bean, Spring 容器無(wú)法完成依賴(lài)注入,因?yàn)?Spring 容器不進(jìn)行緩存prototype作用域的 bean ,因此無(wú)法提前暴露一個(gè)創(chuàng)建中的 bean 示。


向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI