前言
何为循环引用?一个类A引用类B,而B又引用A,导致两个类互相引用。spring有多种依赖注入方式,最主要的就是setter和构造注入。针对singleton的setter注入,spring为我们解决了循环引用的问题。但是针对构造注入,spring也无能为力,只能抛出BeanCurrentlyInCreationException
,下面来分析一下源码。
构造注入
分别有两个类User2、Address2分别持有对方的引用:
public class Address2 implements Serializable {
public Address2(User2 user2) {
System.out.println("address2 begin");
}
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
public class User2 implements Serializable{
public User2(Address2 address2) {
System.out.println("User2 begin");
}
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
applicationContext.xml的配置:
<bean id="user" class="com.pingan.instance.User2">
<constructor-arg index="0" ref="address"/>
</bean>
<bean id="address" class="com.pingan.instance.Address2">
<constructor-arg index="0" ref="user"/>
</bean>
启动spring,立马抛出BeanCurrentlyInCreationException
异常:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'address' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'user' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'user': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:634)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:140)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1139)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1042)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 17 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'user': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 29 more
仔细看下错误堆栈,发现就是在resolveReference
的时候去解析构造函数引发的报错。下面开始分析代码,直接定位到AbstractApplicationContext->finishBeanFactoryInitialization->preInstantiateSingletons->getBean
方法,开始实例化对象,在getBean
中有一段专门针对singleton实例化的代码(不清楚spring整个加载机制的,可以自行查找资料,这里就不展开了);
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
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);
}
如果这个类是singleton(默认scope=singleton),则通过getSingleton
获取实例,里面用到了一个回调方法,也就是说当getSingleton()->getObject()
的时候会触发createBean
操作。下面跟进createBean->doCreateBean
方法,正式开始Bean的初始化操作,里面有几个核心方法:
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
...
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
里面的createBeanInstance
是专门通过构造函数实例化对象的,没错,这里就是解析构造注入
的地方,直接定位到autowireConstructor->ConstructorResolver.autowireConstructor->resolveConstructorArguments->resolveValueIfNecessary->resolveReference
针对构造函数注入,首先要解析构造函数参数,然后解析注入对象:
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
try {
String refName = ref.getBeanName();
refName = String.valueOf(doEvaluate(refName));
if (ref.isToParent()) {
if (this.beanFactory.getParentBeanFactory() == null) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Can't resolve reference to bean '" + refName +
"' in parent factory: no parent factory available");
}
return this.beanFactory.getParentBeanFactory().getBean(refName);
}
else {
Object bean = this.beanFactory.getBean(refName);
this.beanFactory.registerDependentBean(refName, this.beanName);
return bean;
}
}
catch (BeansException ex) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
}
}
resolveReference
中最终还是通过beanFactory.getBean
去获取bean的实例,那到底是在哪里抛出异常的呢?回到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) {
...
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<Exception>();
}
try {
//回调方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
...
}
catch (BeanCreationException ex) {
...
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
在singletonFactory.getObject
之前执行了一个beforeSingletonCreation
方法:
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
原来是singletonsCurrentlyInCreation
中已经存在该beanName了,大致清楚了。
结论:当我们去实例化User2的时候,先把User2加入到singletonsCurrentlyInCreation
中,然后去解析构造函数的时候发现Address2
没有实例化,然后通过getBean
去实例化,并把它加入到singletonsCurrentlyInCreation
中,解析Address2
的时候发现User2
又没有实例完成,去实例的时候发现User2已经在实例化过程中了,只能抛出异常,出现循环引用,此时的spring也无能为力,只能叹息一声:"为何要用构造注入,你不知道它有循环注入的问题吗?"。既然如此,那setter注入是怎么解决循环引用的问题?
setter注入
public class User implements Serializable{
public User() {
System.out.println("User begin");
}
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
public class Address implements Serializable {
public Address() {
System.out.println("address begin");
}
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
applicationContext.xml:
<bean id="user" class="com.pingan.instance.User">
<property name="address" ref="address"/>
</bean>
<bean id="address" class="com.pingan.instance.Address">
<property name="user" ref="user"/>
</bean>
大体流程和构造注入一致,只是一个是解析构造函数,一个是通过setter.定位到AbstractAutowireCapableBeanFactory->doCreateBean
中的populate
方法:
try {
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
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);
}
}
跟进去populateBean
,里面针对了不同的注入类型进行了解析AUTOWIRE_BY_NAME、AUTOWIRE_BY_NAME、AUTOWIRE_BY_TYPE
.我们这里是通过setter注入的,所以重点关注最下面的applyPropertyValues
方法:
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
...
String propertyName = pv.getName();
Object originalValue = pv.getValue();
//核心方法
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
// Possibly store converted value in merged bean definition,
// in order to avoid re-conversion for every created bean instance.
if (resolvedValue == originalValue) {
if (convertible) {
pv.setConvertedValue(convertedValue);
}
deepCopy.add(pv);
}
else if (convertible && originalValue instanceof TypedStringValue &&
!((TypedStringValue) originalValue).isDynamic() &&
!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue);
deepCopy.add(pv);
}
else {
resolveNecessary = true;
deepCopy.add(new PropertyValue(pv, convertedValue));
}
BeanDefinitionValueResolver
就是解析器,负责解析注入的参数,valueResolver.resolveValueIfNecessary(pv, originalValue)
和构造注入的一样,正式开始解析参数,最后调用了resolveReference
,最终又会通过getSingleton
来获取实例。
重点就是上面的getSingleton
这个方法,首先会读取singletonObjects
缓存中的实例,如果存在则直接返回。因为当我们实例化完成的时候,会通过addSingleton
加入到缓存,所以Address注入User的时候不会重新加载一遍,只是从缓存中直接读取,所以不会有循环引用的问题。
总结
对spring循环引用的问题有了个基本的认识,如果项目中出现这种问题也能快速的定位,不至于惊慌失措.