所谓循环引用是指对象之间的互相依赖形成了一个闭环,比如A依赖B,B依赖C,而C又反过来依赖A,这个是关于循环引用的基本认知。然后在Spring中循环引用又可细分成三种场景,我们逐一来分析下。
一. 单例bean基于构造函数的循环引用
1). bean定义
<!-- 基于构造函数的循环引用-->
<bean id="circleA" class="entity.CircleA">
<constructor-arg index="0" ref="circleB" />
</bean>
<bean id="circleB" class="entity.CircleB">
<constructor-arg index="0" ref="circleC" />
</bean>
<bean id="circleC" class="entity.CircleC">
<constructor-arg index="0" ref="circleA" />
</bean>
上面一共定义三个对象,分别在自己的构造函数中引用其它对象。
2). 执行结果
执行失败,Spring会抛出异常。
3). 原因分析
Spring创建一个对象大致可以分为四个步骤,分别是“实例化”,“填充属性值”,“初始化”,“登记善后处理”,“注册单例Bean”(上述步骤暂时不考虑AOP的情况),这几个步骤后续博客会做详细分析,这里不展开了。根据上面的配置,Spring首先会尝试创建A对象,由于调用是非默认构造函数,所以实例化之前需要先获取实参对象(也就是B对象),Spring会先把正在实例化的A对象的ID置入DefaultSingletonBeanRegistry类的Set<String> singletonsCurrentlyInCreation集合中,然后去创建B对象。
在实例化B对象的过程中,由于调用的也是非默认构造函数,所以Spring会把正在实例化的B对象的ID置入缓存中,然后尝试去创建C对象。实例化C对象的步骤跟上面一样,Spring发现实例化C对象必须要先创建A对象,会先把C对象的ID置入缓存。目前缓存中已经有A,B,C三个对象的ID。
然后Spring又重新尝试去创建A对象,发现对B对象有依赖,当Spring把A对象的ID再次置入缓存中时,发现缓存中已经存在A对象的ID,于是乎,Spring觉得这是个死循环,就抛出异常了。
二. 单例bean基于setter的循环引用
1). bean定义
<!-- 基于setter的循环引用-->
<bean id="circleA" class="entity.CircleA" >
<property name="circleB" ref="circleB"/>
</bean>
<bean id="circleB" class="entity.CircleB" >
<property name="circleC" ref="circleC"/>
</bean>
<bean id="circleC" class="entity.CircleC" >
<property name="circleA" ref="circleA"/>
</bean>
2). 执行结果
执行成功
3). 原因分析
Spring首先实例化A对象,由于调用是的默认构造函数,在实例化这个步骤不存在对其它对象的依赖,所以A对象实例化成功,然后Spring创建一个ObjectFactory接口的实现类,并把它置入DefaultSingletonBeanRegistry类的Map<String, ObjectFactory> singletonFactories中。
实例化A对象成功后,进入创建对象的第二个步骤,“填充属性值”,此时Spring发现需要先创建B对象,过程跟创建A对象一样,实例化B对象是成功的,在填充B对象的属性值时发现对C对象有依赖,然后创建C对象,实例化C对象成功后,开始填充C对象的属性,发现C对象对A对象有依赖,此时Spring根据A对象的ID从singletonFactories中获取ObjectFactory接口实现类,调用其getObject()方法获取A对象,然后把A对象赋值给C对象的属性,C对象完成了创建过程,再把C对象赋值给B对象的属性,B对象完成了创建过程,再B对象赋值给A对象的属性,至此A对象也创建成功了。
补充说明下,在调用ObjectFactory#getObject()获取A对象时,如果存在实例化敏感bean后处理器(SmartInstantiationAwareBeanPostProcessor)的话,会调用处理器的getEarlyBeanReference(Object bean, String beanName)方法,对此时尚是半成品的A对象执行后处理操作。
三. 属性bean基于setter的循环引用
1). bean定义
<!-- 基于setter的非单例循环引用 -->
<bean id="circleA" class="entity.CircleA" scope="prototype">
<property name="circleB" ref="circleB"/>
</bean>
<bean id="circleB" class="entity.CircleB" scope="prototype">
<property name="circleC" ref="circleC"/>
</bean>
<bean id="circleC" class="entity.CircleC" scope="prototype">
<property name="circleA" ref="circleA"/>
</bean>
2). 执行结果
执行失败,Spring会抛出异常。
3). 原因分析
A对象实例化成功后,由于是非单例Bean,所以并不会被缓存起来,从而导致死循环。