Spring如何解决循环依赖问题
现象:
循环依赖其实就是循环引用,也就是两个或者两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。
如何理解依赖?在spring中有:
(1)构造器循环依赖:结果:项目启动失败,发现了一个cycle
@Service
public class A {
public A(B b) { }
}
@Service
public class B {
public B(C c) {
}
}
@Service
public class C {
public C(A a) { }
}
(2)field属性注入循环依赖:结果:项目启动成功
@Service
public class A1 {
@Autowired
private B1 b1;
}
@Service
public class B1 {
@Autowired
public C1 c1;
}
@Service
public class C1 {
@Autowired public A1 a1;
}
(3)field属性注入循环依赖(prototype):结果:项目启动失败,发现了一个cycle。
@Service
@Scope("prototype")
public class A1 {
@Autowired
private B1 b1;
}
@Service
@Scope("prototype")
public class B1 {
@Autowired
public C1 c1;
}
@Service
@Scope("prototype")
public class C1 {
@Autowired public A1 a1;
}
现象总结:同样对于循环依赖的场景,构造器注入和prototype类型的属性注入都会初始化Bean失败。因为@Service默认是单例的,所以单例的属性注入是可以成功的。
分析原因:也就是在发现SpringIIOC的过程。
总结:有人问:为什么SingletonBeanFactory只是一个三级缓存,那么一级缓存和二级缓存有什么作用呢?
其实只要理解整个流程就可以切入了,Spring在初始化Singletion的时候大致可以分为几步,初始化——>设置值——>销毁,循环依赖的场景只有A——>B——>A这样的顺序,但是在并发的场景下,每一步在执行时,都有可能调用getBean方法,而单例的Bean需要保证只有一个instance,那么Spring就是通过这些个缓存外加对象锁去解决这类问题,同时可以省去不必要的重复操作。Sring的锁的粒度选择也是很厉害。
解决此类问题的关键就是要对SpringIOC和DI的整个流程了解,源码一般情况下不要求每一行代码都了解透彻,但是对于整个的流程和每个流程中在做什么事都需要了解,这样实际遇到问题才能可以很快的切入进行分析解决。