Spring中bean的实例化方法主要是 prototype和singleton两种。其中singleton的循环依赖包括两种:构造器循环依赖,和setter的循环依赖。prototype中的循环依赖。
1.singleton 构造器循环依赖
代码示例:
public class CycleA {
private CycleB cycleB;
public CycleA(CycleB cycleB) {
this.cycleB = cycleB;
}
public void doSomething() {
System.out.println("this is the CycleA ... ");
}
}
public class CycleB {
private CycleC cycleC;
public CycleB(CycleC cycleC) {
this.cycleC = cycleC;
}
public void doSomething() {
System.out.println("this is the CycleB ...");
}
}
public class CycleC {
private CycleA cycleA;
public CycleC(CycleA cycleA) {
this.cycleA = cycleA;
}
public void doSomething() {
System.out.println("this is the CycleC ...");
}
}
配置文件示例:
<bean id="cycleA" class="com.test.bean.blog.myCycleDependencies.bean.CycleA">
<constructor-arg name="cycleB" ref="cycleB"/>
</bean>
<bean id="cycleB" class="com.test.bean.blog.myCycleDependencies.bean.CycleB">
<constructor-arg name="cycleC" ref="cycleC"/>
</bean>
<bean id="cycleC" class="com.test.bean.blog.myCycleDependencies.bean.CycleC">
<constructor-arg name="cycleA" ref="cycleA"/>
</bean>
这个是一个Singleton构造器循环依赖的实例。构造器的循环依赖无法解决。CycleA依赖于CycleB,CycleB依赖于CycleC 。CycleC依赖于CycleA。在创建的时候,当构造CycleC的时候需要去创建CycleA。这个时候这个时候需要判断出发生了循环。
关键点:一、当创建CycleC的时候,需要创建CycleA,是在哪里被触发的。二、如何发现CycleA是一个循环依赖的。
- 如何发现CycleA是一个循环依赖的
可以看一下代码:
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 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 newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<Exception>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
以上代码是获取一个单例对象。首先Object singletonObject = this.singletonObjects.get(beanName);
获取到对应的bean。如果获取失败,就开始调用beforeSingletonCreation(beanName);
开始创建。
代码如下:
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
其中由于CycleA在最开始创建的时候已经被写入到this.singletonsCurrentlyInCreation中了,这个beanName被写入到set中了。在创建过程中这个对象依赖到自身了,返回false。通过一个Set来记录下创建当前bean中依赖的bean。如果发现自己依赖自己就是一个循环依赖。
- 当创建CycleC的时候,需要创建CycleA,是在哪里被触发的。
在通过CycleC的构造函数来创建bean的时候,发现会依赖CycleA的实例,这个时候首先创建CycleA的对象。具体代码如下:
protected BeanWrapper autowireConstructor(
String beanName, RootBeanDefinition mbd, Constructor<?>[] ctors, Object[] explicitArgs) {
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
将通过构造器来生成一个BeanWrapper的工作委托给ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs)
。这个过程中需要将构造器中依赖的CycleA先实例化出来。
这个工作委托给BeanDefinitionValueResolver
类中的public Object resolveValueIfNecessary(Object argName, Object value)
方法
看下这个方法的实现由于在配置文件中定义了ref,在Spring内部被转换为RuntimeBeanReference对象:
具体的实现是通过BeeanFactory.getBean()来获取到所依赖的CycleA的实例。
具体如下第351行:
- 总结
以上就是Spring中单例构造器循环依赖的过程。在开始创建CycleA的时候,在准备实力化的时候将CycleA写到singletonsCurrentlyInCreation
中,表示当前的bean对象开始实例化。
然后开始创建的过程中,当将一个CycleA创建为一个BeanWrapper的时候,首先需要CycleA的构造函数中依赖的CycleB的对象,这个时候就开始创建CycleB,这个时候也会重复上面的过程将CycleB写到singletonsCurrentlyInCreation
。
往复以上过程,又一次创建CycleA的时候,发现singletonsCurrentlyInCreation
已经有了一个对象,表示一个对象循环依赖了。抛出异常。
2.prototype 循环依赖
代码示例还是之前的示例,将每个bean的scope改为prototype
类型。
prototype循环依赖的处理过程如下,由于prototype只负责创建一个对象。循环依赖的判定逻辑是在创建CycleA的时候,发现依赖了CycleA,在多线程中,可能会出现这种情况,但是这种并不是循环依赖,所以使用ThreadLocal来隔离开不同的循环依赖。
在创建对象之前先检查是否是循环依赖了:
看下具体实现:
从ThreadLocal中获取到对应beanName是否在创建,如果在创建,就抛出异常。其中,当只有一个beanName的时候使用的String,当有多个beanName的时候存的是Set。
在创建之前将beanName的信息写到ThreadLocal中去:
和上面相同,需要判断是一个还是多个,多个需要将String转变为Set。
在创建完成的额时候需要将beanName从ThreadLocal中删除:
总结
prototype循环依赖主要是使用ThreadLocal来记录当前bean的依赖情况,创建之前先判断一下是否循环依赖,如果没有循环依赖将当前的beanName写到ThreadLocal中去。在创建完成的时候将当前beanName删除。
Singleton的setter循环依赖
相对于基于Singleton构造器循环依赖和prototype中判断当前bean是否正在创建set中,如果在就直接抛出异常。Singleton的setter循环依赖中判断是否是循环依赖也是用了和构造器相同的方法,但是将创建一个对象的过程拆分为两个步骤:initBean
和popuBean
。initBean
:主要负责生成一个Bean的实例,这个实例中的属性可能还没有注入。popuBean
向bean中注入属性,在获取属性的过程中可能会循环的去获取其他的bean。(Spring中对于属性的获取都是委托给BeanDefinitionValueResolver
处理的。)
以 CycleA—> CycleB —> CycleC —> CycleA 。 这样一个循环依赖。这些加载的过程是如下过程:(注:initCycleA表示实例化CycleA;popuCycleA表示填充CycleA)。
以上:Spring在实例化的时候是一个递归的过程。在popuCycleA的时候,这个时候发起了initCycleB,然后开始popuCycleB,又递归的发起了initCycleC,然后popuCycleC,这个当中,由于CycleA已经初始化完成了,已经能够返回CycleA的实例。能够完成popuCycleC。然后递归的完成popuCycleB,然后完成popuCycleA。最终将所有的bean初始化完成。
Spring中在处理单例的时候所有的方法都是委托给DefaultSingletonBeanRegistry
处理的。这个当中有三个关键的方法来帮助解决Spring中的循环依赖。
- public Object getSingleton(String beanName)
这个类是在BeanFactory.getBean最开始的地方调用的。
按照注释,返回的实例是可能是一个完全实例化的bean也是一个为注入属性的bean。在DefaultSingletonBeanRegistry中不同的Bean将会被放在不同的容器中。这个后面再介绍。
- public Object getSingleton(String beanName, ObjectFactory singletonFactory)
在这个方法中ObjectFactory中getObject方法提供了一个创建Bean的方法。
以上在createBeanean()是创建一个Bean的方法。createBean做了两件事,initBean和popuBean。在做这两件事情的中间调用了第三个接口
- protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory)
如上图,这个方法调用的时机在initBean完成以后,在popuBean之前。这样在popuBean的过程中通过第一个方法获取到bean的实例的时候就能够获取到bean,而不用重新创建,触发循环校验的机制了。
Spring中单例的对象从创建到完成之间尽力的形态:
DefaultSingletonBeanRegistry中维护了三个Map来存放Bean
Map<String, Object> singletonObjects
//缓存了所有的完全实例化成功的bean
Map<String, ObjectFactory<?>> singletonFactories
//缓存了当前所有的ObjectFactory。这个ObejctFactorygetObject()中的方法是一个已经实例化的bean
Map<String, Object> earlySingletonObjects
//这个bean中缓存的bean是一个已经实例化完成,但是还没有完全pop的bean。
总结
解决单例setter循环依赖的方式主要是将实例化bean的过程分为initBean 和popuBean过程,在这两个阶段之间调用一下第二个方法将bean注入到DefaultSingletonBeanRegistry中去。这样在循环依赖的时候就能够获取到依赖的对象的引用,而不用在一次去再一次initBean,发生循环依赖。
ObjectFactory这个接口是DefaultSingletonBeanRegsitry提供的一个获取bean的策略,这样能够使得类更加的通用。