什么是循环依赖?
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。
这里说的不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件。
Spring中循环依赖场景主要有以下两种:
(1)field属性的循环依赖
(2)构造器的循环依赖
(3)DependsOn循环依赖
SPring是如何解决循环依赖的?
这时候我们需要先了解Spring内部的三级缓存:
- singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例
- earlySingletonObjects 二级缓存,用于保存实例化完成的bean实例
- singletonFactories 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象
下图展示的就是Spring解决循环依赖的过程:
@Service
public class TestService1 {
@Autowired
private TestService2 testService2;
public void test1() {
}
}
@Service
public class TestService2 {
@Autowired
private TestService1 testService1;
public void test2() {
}
}
这就是具体解决循环依赖的代码。
TestService1先准备实例化,如何需要注入TestService2的实例化对象;同时TestService2中也需要实例化TestService1的实例对象。
TestService1实例时由于在一级缓存中找不到实例,就创建实例如何写入到三级缓存中;
如何注入TestService2需要实例,同样在一级缓存中找不到实例,也创建实例如何写入到三级缓存中;
这时TestService2中需要注入TestService1的实例,可以从二级缓存中获取到实例,如何就可以进入二级缓存中;
如何TestService2实例成功,转入一级缓存中。
现在TestService1也就可以得到TestService2的实例了,初始化完成,直接写入一级缓存,这样就解决了循环依赖的问题。
以两级缓存的方式解决
上面的方式是利用了三级缓存来处理,但其实我们可以使用两级缓存来解决这个问题。
我们可以抛开第三级缓存,只考虑一二级缓存。
TestService1实例时在一级缓存中找不到TestService1的实例对象,就直接创建实例并写入二级缓存,后续步骤和上面差不多,只是在找不到实例的时候创建实例并写入到二级缓存,跳过了三级缓存,这样也并不会影响我们的过程执行。