一、什么是依赖循环?
举个例子,两个或以上Bean,A需要B,B需要C,C需要A,于是形成了一个循环:尝试创建A-需要B-尝试创建B-需要C-尝试创建C-需要A-尝试创建A……以此往复。
在Spring中循环依赖一共可以分为两种情况:
1.构造器循环依赖。例如:
public class A {
private B b;
public A() {
this.b = new B(this);
}
}
public class B {
private A a;
public B(A a) {
this.a = a;
}
}
2.属性循环依赖,例如:
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
public class B {
private A a;
public void setA(A a) {
this.a = a;
}
其中第二类又可以细分为,单例模式下的循环依赖和prototype即多例下的循环依赖,而三种情况中,spring仅提供了对单例模式下属性循环依赖的解决方法。
二、如何解决依赖循环
以两个类为例,A、B两个类相互依赖,初始化A的时候,第一步实例化A完成(生成对象工厂实例放入三级缓存),注入依赖属性B,一级缓存查询B没有,二级缓存查询B没有,
初始化B(生成对象工厂实例放入三级缓存),注入依赖属性A,一级缓存查询A没有,二级缓存查询A没有,三级缓存查询到A的对象工厂,需要AOP增强则生成A的代理对象,没有则直接创建A实例对象,并将A放入到二级缓存,注入A的代理对象完成,生成代理对象B,B移入一级缓存。
继续A属性注入(B的代理对象),然后可能还会依赖注入C对象流程和B一致,所有依赖注入完成后A初始化,生成A的代理对象,发现A的代理对象已存在,则跳过,放入一级缓存。此时A的代理对象也是提前生成的,但是仅针对循环依赖提前生成。
具体使用如下:
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
或:
@Component
public class A {
private B b;
@Autowired
public void setB(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
@Autowired
public void setA(A a) {
this.a = a;
}
}
因为spring的bean默认的就是单例模式,我们只要确保没有在构造器中形成循环依赖就可以解决大部分问题。
如果无法防止构造器中形成依赖循环,我们也可以通过@Lazy注解来解决,例如:
@Component
public class A {
private B b;
@Autowired
public A(@Lazy B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
@Autowired
public B(@Lazy A a) {
this.a = a;
}
}
这样spring不会直接生成一个a或者b的对象,而是先给一个代理对象,当真正需要使用时,才真正使用代理对象来生成a或b的对象,以此解决构造器的循环依赖问题。