Spring 对于循环依赖的处理
2019-04-16 本文已影响4人
谁说咖啡不苦
什么是循环依赖
循环依赖就是循环引用,就是两个或者多个bean相互之间的持有对方。
循环依赖的类型
- 构造器循环依赖
- setter循环依赖
我们先来看看如下的例子:
public class First {
private Second second;
public void a () {
second.b();
}
public Second getSecond() {
return second;
}
public void setSecond(Second second) {
this.second = second;
}
}
public class Second {
private Third third;
public void b() {
third.c();
}
public Third getThird() {
return this.third;
}
public void setThird(Third third) {
this.third = third;
}
}
public class Third {
private First first;
public void c() {
first.a();
}
public void setFirst(First first) {
this.first = first;
}
public First getFirst() {
return this.first;
}
}
我们先来看看上面例子的两种循环依赖的情况。
构造器循环依赖
我们Bean的初始化是方式可以通过如下的xml过程.
<bean id="first">
<constructor-arg index="0" ref="second"/>
</bean>
<bean id="second">
<constructor-arg index="0" ref="third"/>
</bean>
<bean id="third">
<constructor-arg index="0" ref="first"/>
</bean>
Spring容器将每一个正在创建的bean标识符放在一个“当前正在创建bean的池”中,bean标识符在创建的过程中将一直保持在这个池中,因此,如果在创建bean过程中发现自己已经在这个池中,将会抛出'BeanCurrentlyInCreationException'异常标识循环依赖无法处理;而对于创建完毕的bean将会从池中清除。
针对上述的这个例子,我们来理一下这个过程:
- Spring 容器创建"first"bean,首先会去池中查找是否已经创建当前的“first”bean,如果没有发现,进行调用构造器创建,这个时候,构造器需要参数second进行创建,因此,就要进行一个创建second的bean,同时,由于当前first是刚好处于一个正在创建的过程中,所以,我们要将first放入到我们这个“当前正在创建bean的池”中。
- 由第一步的构造器依赖,我们要接着创建second的bean,过程和first的一样,先进行一个判断是否存在second在池中,没有的话,就进行构造器创建,有依赖参数,就依赖参数的bean的创建,同时,将second放入池中。
- 同样的和上述的过程一样,但是,在这个时候,我们要进行构造器创建bean的时候,需要依赖first,那么我们按照上面的逻辑不走往下走的过程应该就是去创建first,那这个时候,这一步就回到了第一步的动作。但是,在第一步的时候,我们还是要进行一个检查判断是否在池中已经存在first。显然,这个时候,池中已经存在了我们刚刚创建的first的bean,因此,就会抛出BeanCurrentlyInCreationException这个异常了。
所以,我们从上面的过程可以得出一个结论,构造器的循环依赖是没法解决。
setter循环依赖
对于setter注入造成的循环依赖是通过Spring容器提前暴露完成构造器注入,但是未完成其他步骤(如,setter注入)的bean来完成的。简单的说就是,先调用一个无参构造函数,进行一个创建bean的实例,然后在创建完这个实例之后, 在调用相应的setter方法,将依赖填充进去。
对于上述例子,我们使用setter循环依赖的过程是这样的:
- Spring 容器创建单例"first"bean,首先根据无参构造器创建bean first,并暴露一个"ObjectFactory"用于返回一个提前暴露一个创建中的bean,并将first标识放入“当前正在创建bean的池”中,然后进行setter注入second。
- 创建bean second 和第一步同样的动作,然后进行setter注入third。
- 创建bean third和前面的步骤都一样,但是在进行setter注入first的时候,由于提前暴露了ObjectFactory工厂,从而使它能够返回提前创建中的一个bean。
- 最后在完成对third,second的setter注入。
所以,通过setter注入,是能够解决循环依赖的问题。
Spring对Bean的创建
通过上述的构造器循环依赖的创建bean和setter循环依赖的创建bean,我们可以发现,spring在处理的时候,走的是两种不同的方式进行一个处理。