循环引用只支持在单例情况,此处详细讨论单例
如
参看 AbstractBeanFactory.doGetBean , 在单例创建时不是直接新建实例的,而是传入了回调方法。在创建前后,分别在创建前后加入 singletonsCurrentlyInCreation 状态。
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
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, name, beanName, mbd);
}
具体getSingleton方法如下
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while the singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<Exception>();
}
try {
singletonObject = singletonFactory.getObject();
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
addSingleton(beanName, singletonObject);
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
在如上方法中注意到父类 DefaultSingletonBeanRegistry 几个变量
singletonsCurrentlyInCreation
单例在调用回调方法的前与后,分别通过beforeSingletonCreation及afterSingletonCreation加入和去除beanName
singletonObjects, registeredSingletons
单例完全创建成功后,addSingleton添加这两个参数
singletonFactories
doGetBean 中的回调方法,会调用 createBean,然后调用 AbstractAutowireCapableBeanFactory 中的doCreateBean,
在调用populateBean(beanName, mbd, instanceWrapper);进行属性注入前,有这一段
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>() {
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
这是为了在属性注入前提前暴露未完成的bean实例,当其它实例有对它的引用,即可以在该实例还未属性注入时,就可以作引用赋值
如果是单例且是创建中的状态,则认为需要earlySingletonExposure(早期静态暴露),由于此方法是在beforeSingletonCreation之后afterSingletonCreation之前调用的,则对单例肯定是要做早期静态暴露的。此处调用
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);
}
}
}
在此处singletonFactories赋值,其值为一回调方法,该方法中调用 getEarlyBeanReference,在调用该方法时对方法应用BeanPostProcessors,回调方法的用处就是,不必现在就应用BeanPostProcessors,但是现在就可以把对象暴露出去。
earlySingletonObjects
那把对象暴露出去有什么用呢?在下面这个例子下 TestA,TestB分别使用属性注入的方式握有对方法的引用
<bean id="testA" class="com.test.TestA">
<property name="testB" ref="testB"></property>
</bean>
<bean id="testB" class="com.test.TestB">
<property name="testA" ref="testA"></property>
</bean>
在AbstractAutowireCapableBeanFactory中看到三个方法的调用顺序分别是先调用
1 createBeanInstance ---创建实例
2 addSingletonFactory ---暴露实例引用工厂
3 populateBean --注入属性依赖
在getBean("TestA")时,会在执行populateBean时发现TestB依赖,此时必须要调用getBean("TestB"), 而在调用getBean("TestB")时又会在执行到populateBean时发现需要调用getBean("TestA")
在getBean("TestB")时执行到populateBean时调用getBean("TestA")时,此时情况变了,参考AbstractBeanFactory.doGetBean的getSingleton方法
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);
}
第一次调用getBean("TestA")时,isSingletonCurrentlyInCreation判断为false,不过第二次调用时,由于TestA已经执行了创建实例及暴露引用两步,此时isSingletonCurrentlyInCreation判断为true,此时就会把singletonFactory中的引用取出,执行getObject()方法,把执行了BeanPostProcessors后的实例,放入earlySingletonObjects。然后把实例返回供getBeat("TestB")注入。TestB完成注入后再把实例供TestA注入
在注入完成后,还会经历下面这段验证,见AbstractAutowireCapableBeanFactory.doCreateBean下面,由于TestA会执行两次getBean("TestA")且在第二次时往 earlySingletonObjects 放了对象,所以此处earlySingletonReference不为空
具体调用的链如下
getBean(name=testA)
doGetBean(name=testA, requiredType=null, args=null, typeCheckOnly=false)
getSingleton(beanName=testA, singletonFactory=org.springframework.beans.factory.support.AbstractBeanFactory$1@96def03)
getObject()
createBean(beanName=testA, mbd=Root bean: class [com.codeanalysis.TestA]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], args=null)
doCreateBean(beanName=testA, mbd=Root bean: class [com.codeanalysis.TestA]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], args=null)
populateBean(beanName=testA, mbd=Root bean: class [com.codeanalysis.TestA]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], bw=org.springframework.beans.BeanWrapperImpl: wrapping object [com.codeanalysis.TestA@59d016c9])
applyPropertyValues(beanName=testA, mbd=Root bean: class [com.codeanalysis.TestA]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], bw=org.springframework.beans.BeanWrapperImpl: wrapping object [com.codeanalysis.TestA@59d016c9], pvs=PropertyValues: length=1; bean property testB)
resolveValueIfNecessary(argName=bean property testB, value=<testB>)
resolveReference(argName=bean property testB, ref=<testB>)
getBean(name=testB)
doGetBean(name=testB, requiredType=null, args=null, typeCheckOnly=false)
getSingleton(beanName=testB, singletonFactory=org.springframework.beans.factory.support.AbstractBeanFactory$1@cd3fee8)
getObject()
createBean(beanName=testB, mbd=Root bean: class [com.codeanalysis.TestB]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], args=null)
doCreateBean(beanName=testB, mbd=Root bean: class [com.codeanalysis.TestB]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], args=null)
populateBean(beanName=testB, mbd=Root bean: class [com.codeanalysis.TestB]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], bw=org.springframework.beans.BeanWrapperImpl: wrapping object [com.codeanalysis.TestB@63021689])
applyPropertyValues(beanName=testB, mbd=Root bean: class [com.codeanalysis.TestB]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], bw=org.springframework.beans.BeanWrapperImpl: wrapping object [com.codeanalysis.TestB@63021689], pvs=PropertyValues: length=1; bean property testA)
resolveValueIfNecessary(argName=bean property testA, value=<testA>)
resolveReference(argName=bean property testA, ref=<testA>)
getBean(name=testA)
doGetBean(name=testA, requiredType=null, args=null, typeCheckOnly=false)
getSingleton(beanName=testA)
此时返回 TestA@59d016c9. 实例
由于doCreateBean的过程是先暴露引用,再属性注入,再调用init-method初始化。init-method初始化过程可能会导致exposedObject变化。
如果 exposedObject != bean,则要看有没有依赖了,因为依赖都是依赖的bean引用,如果有,那就报错。
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.");
}
}
}
}