下面就是关于“Spring源码剖析之Spring处理循环依赖的问题”的完整攻略。
标题:Spring源码剖析之Spring处理循环依赖的问题
什么是循环依赖?
循环依赖指的是在Spring容器初始化bean时,A对象依赖B对象,同时B对象又依赖A对象。这种情况下,Spring无法推断依赖关系,会抛出BeanCurrentlyInCreationException异常。
Spring提供了三种处理循环依赖的方法:
- 构造函数方式的循环依赖;
- Set方式的循环依赖;
- 通过AOP CGLIB代理方式的循环依赖。
构造函数方式的循环依赖
构造函数方式的循环依赖是指,两个或多个bean在实例化的时候就互相依赖。当Spring创建Bean时,发现A依赖B,于是会先去创建B对象;而B依赖A,于是也会去创建A对象。这就会导致两个对象互相等待初始化。
针对构造函数方式的循环依赖,Spring提供了一个“提前暴露Bean”(Eagerly-Exposed Bean,EJB)的机制。在将JavaBean实例化的时候,会分为实例化和初始化两步,在初始化过程中注入属性信息。这时候如果A依赖B,而B又依赖A,那么再A实例化完并且成功初始化时,B依赖A的属性已经赋值成功,这时候B就可以正常地初始化注入完成。因此,可以通过将需要提前初始化的 Bean、早些暴露出来,从而解决构造函数方式的循环依赖问题。
Set方式的循环依赖
针对Set方式的循环依赖,Spring采用了一个“两步注入”(two-stage injection)的机制。当Bean初始化完成后,容器会将Bean实例化后的对象放入到“实例化完成”的Map容器中,然后开始Bean初始化的工作。在初始化完成前,Spring还会特殊处理一下当前Bean所需要的其他Bean。当注入Bean B时发现 B 还需要注入 A,而 A 目前还未完成注入,此时Spring先自动将B注入一个代理对象,然后将B放入到“正在初始化的Bean”Map容器中,等到 B 初始化完成后,再让 B 代理对象把 A 赋值给 B,从而解决了循环依赖的问题。
AOP CGLIB代理方式的循环依赖
当依赖关系出现循环调用时,我们还可以采用 AOP+CGLIB 的方式控制依赖关系的实例化和属性注入。Spring利用AOP的技术,对需要循环回调的Bean通过CGLIB动态代理构建一个代理对象叫做“本地代理”,然后使用本地代理对象解析AOP方法时注入所依赖的Bean对象。
示例说明
下面是两个示例说明,分别说明了构造函数方式和Set方式的循环依赖。
示例1:构造函数方式的循环依赖
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
public class B {
private A a;
public B(A a) {
this.a = a;
}
}
@Configuration
public class AppConfig {
@Bean
public A a() {
return new A(b());
}
@Bean
public B b() {
return new B(a());
}
}
当我们在执行AppConfig
的@Bean
时,就会抛出循环依赖的异常。
如何解决呢?我们可以使用@Configuration
的属性@DependsOn
来解决。
@Configuration
public class AppConfig {
@Bean
public A a() {
return new A(b());
}
@Bean
@DependsOn("a")
public B b() {
return new B(a());
}
}
在这个例子中,我们告诉Spring在初始化Bean b的时候,先初始依赖的Bean a,这样就不会再产生循环依赖的问题。
示例2:Set方式的循环依赖
public class A {
private B b;
public A() {
}
public void setB(B b) {
this.b = b;
}
}
public class B {
private A a;
public B() {
}
public void setA(A a) {
this.a = a;
}
}
如果我们将上面的A和B两个类放入Spring容器中,即:
<bean id="a" class="A" />
<bean id="b" class="B" />
则会抛出异常提示:
Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
解决方式:
我们可以使用@Autowired
或@Resource
来注入依赖,如下。
public class A {
@Resource
private B b;
}
public class B {
@Autowired
private A a;
}
这样,Spring会在执行到A或B的时候,先实例化自己再注入依赖的Bean,从而避免了循环依赖的问题。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring源码剖析之Spring处理循环依赖的问题 - Python技术站