引言
之前的幾篇對(duì)Spring IOC
源碼分析的文章,大體上把IOC
容器內(nèi)部實(shí)現(xiàn)做了分析腥例,但在有些細(xì)節(jié)上并沒(méi)有很深入的去分析辅甥。本篇文章主要是分析Spring IOC
容器對(duì)Bean之間的循環(huán)依賴
是如何解決的
什么是循環(huán)依賴
那么什么是循環(huán)依賴呢?簡(jiǎn)單的理解一下院崇,A依賴B肆氓,B又依賴A,這就構(gòu)成了一個(gè)最簡(jiǎn)單的循環(huán)依賴底瓣,為了幫助大家理解,新建兩個(gè)互相依賴的類(兒子和爸爸互相依賴沒(méi)有錯(cuò)吧 ..)
public class Father {
private String name;
private Son son;
}
public class Son {
private String name;
private Father father;
}
這兩個(gè)bean交給Spring管理
<bean id="son" class="com.wangjn.demo.impl.Son">
<property name="name" value="son"></property>
<property name="father" ref="father"></property>
</bean>
<bean id="father" class="com.wangjn.demo.impl.Father">
<property name="name" value="father"></property>
<property name="son" ref="son"></property>
</bean>
啟動(dòng)Spring IOC
容器蕉陋,用getBean
方法可以成功獲取son
對(duì)象捐凭,并且也注入了father
對(duì)象,可見Spring
為我們解決了循環(huán)依賴的問(wèn)題凳鬓∽鲁Γ可是按照正常創(chuàng)建Bean
的流程來(lái)說(shuō),這個(gè)過(guò)程將會(huì)是一個(gè)死循環(huán)缩举,因?yàn)樵趧?chuàng)建son
對(duì)象為son
注入father
屬性時(shí)垦梆,就會(huì)去獲取father
對(duì)象,而在獲取father
對(duì)象賦值son
屬性的時(shí)候仅孩,又會(huì)去獲取son
對(duì)象托猩,從而就陷入了死循環(huán),然后程序崩潰辽慕。
可是結(jié)果并不是我們預(yù)料的那樣京腥,接下來(lái)就來(lái)分析Spring
是如何解決這個(gè)問(wèn)題的
Spring 如何解決循環(huán)依賴
之前對(duì)IOC
源碼分析的文章中有分析過(guò)Bean
的創(chuàng)建過(guò)程,下面我將對(duì)循環(huán)依賴實(shí)現(xiàn)的某些細(xì)節(jié)作分析
Spring 用緩存解決循環(huán)依賴
讓我們回到AbstractBeanFactory
的doGetBean
方法溅蛉,doGetBean
方法就是我們通過(guò)容器getBean
方法實(shí)際調(diào)用的邏輯公浪,我們?cè)谶@里著重關(guān)注getSingleton
方法他宛,之前的分析中有提到,調(diào)用getSingleton(beanName)
方法的目的是為了從緩存中直接獲取已經(jīng)創(chuàng)建的Bean欠气,而不必重復(fù)去創(chuàng)建√鳎現(xiàn)在讓我們進(jìn)到getSingleton
方法里面去看看它都做了啥,從哪個(gè)緩存取到了Bean對(duì)象
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;
// 從緩存中獲取 bean
Object sharedInstance = getSingleton(beanName);
... 省略其他創(chuàng)建bean的代碼
}
getSingleton方法
public Object getSingleton(String beanName) {
// 默認(rèn)都是允許提前暴露對(duì)象
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 從創(chuàng)建完成的bean緩存中獲取bean
Object singletonObject = this.singletonObjects.get(beanName);
// 判斷該bean是否仍在創(chuàng)建中预柒,意思是Bean已經(jīng)完成實(shí)例化讯检,但還不完整。屬性還未完全注入
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 從提前暴露的Bean緩存容器(earlySingletonObjects)中獲取
singletonObject = this.earlySingletonObjects.get(beanName);
// 仍未獲取到則從singletonFactories緩存中獲取
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 加入到提前暴露Bean緩存(earlySingletonObjects)中
this.earlySingletonObjects.put(beanName, singletonObject);
// 從singletonFactories緩存中移除
this.singletonFactories.remove(beanName);
}
}
}
}
// 返回對(duì)象卫旱,這里返回的不一定是完全創(chuàng)建的對(duì)象
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
從getSingleton
方法中我們需要著重關(guān)注幾個(gè)Bean的緩存人灼,標(biāo)題已經(jīng)說(shuō)了,緩存是解決循環(huán)依賴的關(guān)鍵顾翼,下面我介紹一下上面代碼中提到了三種緩存
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
-
singletonObjects
用于存放創(chuàng)建完成的單例對(duì)象 -
singletonFactories
用于存放對(duì)象工廠類投放,這里是解決循環(huán)依賴的 -
earlySingletonObjects
用于存放提前暴露的單例對(duì)象。指的是已經(jīng)完成Bean的實(shí)例化适贸,但還未完成屬性注入的不完整對(duì)象
再來(lái)說(shuō)上面代碼中取緩存的步驟灸芳,首先肯定是從singletonObjects
中獲取完全創(chuàng)建完成的Bean對(duì)象,如果獲取不到拜姿,則從提前暴露對(duì)象緩存(earlySingletonObjects
)中獲取烙样,還獲取不到再到singletonFactories
中獲取
到這里為止,我們只分析了取Bean緩存的過(guò)程蕊肥,所以接下來(lái)我們要分析的就是放緩存的過(guò)程代碼
提前暴露Bean
現(xiàn)在讓我們?nèi)サ絼?chuàng)建Bean的過(guò)程谒获。如果緩存沒(méi)取到,會(huì)執(zhí)行創(chuàng)建Bean的邏輯壁却,找到AbstractAutowireCapableBeanFactory
類的doCreateBean
方法批狱,這個(gè)方法在之前的文章中有做過(guò)分析,但沒(méi)有對(duì)Bean緩存處理做分析展东。這里我們著重看中間解決循環(huán)依賴的那部分
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// Instantiate the bean.
// 封裝bean的容器
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 這里是創(chuàng)建 BeanWrapper
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
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(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// 判斷是否需要提前暴露對(duì)象的引用赔硫,用于解決循環(huán)依賴
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<Object>() {
@Override
public Object getObject() throws BeansException {
// 這里會(huì)與AOP相關(guān)
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 依賴注入的主邏輯
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 執(zhí)行一些初始化的方法
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(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
通過(guò)看代碼,可知在Bean完成實(shí)例化之后盐肃,注入屬性之前爪膊,Spring
就將這個(gè)不完整的Bean
放到了singletonFactories
緩存中,從而讓這個(gè)Bean
提前進(jìn)行了暴露砸王。這樣子在后續(xù)的屬性注入操作中推盛,如果存在循環(huán)依賴,就會(huì)從緩存中獲取到這個(gè)提前暴露的Bean
处硬,從而可以順利完成依賴注入小槐。但是要注意這時(shí)候注入的對(duì)象是不完整的,但是因?yàn)橐蕾嚪揭呀?jīng)持有它的引用,所以后續(xù)對(duì)象的完整性是可以保證的
總結(jié)
本篇文章主要從Spring
對(duì)Bean
的緩存層面分析了其對(duì)循環(huán)依賴
的解決凿跳,雖然是Spring
幫我們解決了這個(gè)問(wèn)題件豌,但是對(duì)于實(shí)現(xiàn)的邏輯我們?nèi)匀粦?yīng)該去了解,譬如控嗜,通過(guò)查看源碼可知Spring
僅僅對(duì)單例類型的循環(huán)依賴進(jìn)行解決茧彤,對(duì)于有狀態(tài)的Bean
Spring并沒(méi)有去做處理,而是直接跑出異常疆栏,這些都是需要注意的曾掂。
Spring 系列