一.什么是循环依赖?
是一个或多个对象实例之间存在直接或间接的依赖关系,这种关系构成了一个环形调用。
第一种情况:自己依赖自己
A对象自己依赖自己
第二种情况:两个对象之间的直接依赖
A对象和B对象相互依赖,形成循环
第三种情况:多个对象之间的间接依赖,形成循环
例子:两个对象之间的直接依赖
@Service
public class Service1 {
@Autowired
private Service2 service2;
public void test1(){
}
}
@Service
public class Service2 {
@Autowired
private Service1 service1;
public void test2(){
}
}
循环依赖的场景
所以不是所有的循环依赖Spring都可以解决
单例的setter注入,是目前我们用的最多的,就是上面展示的两个对象的直接依赖
这种依赖,Spring默默的帮我们解决了。
Spring内部有三级缓存:
- singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例
- earlySingletonObjects 二级缓存,用于保存实例化完成的bean实例
- singletonFactories 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。
这三级缓存对应的源码:
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 一级缓存
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 二级缓存
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16); // 三级缓存
单例Bean初始化完成,要经历三步:
实例化 属性赋值 初始化
我们用最常用的单例setter注入来解释一下三级缓存解决循环依赖的过程
1.创建A实例,实例化的时候先把A对象工厂放入三级缓存,三级缓存就是用来保存工厂的,没有经历过一二级缓存,还不完整,先曝光,让我们它有了,
2.第二步,属性赋值,注入就发生在这,因为内部依赖了B,B还没有被创建出来,去实例化B
3.同时B也走到了第二步时,内部依赖了A对象,然后会从一到三级缓存去查A,因为A对象工厂先放到三级缓存曝光,还不完善,但有,把A放入二级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入⼀级缓存。
4.接着A继续属性赋值,顺利从⼀级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除⼆级缓存中的A,同时把A放⼊⼀级缓存
5.最后,⼀级缓存中保存着实例化、初始化都完成的A、B对象
实例化和属性赋值是分开的,所以Spring能解决setter注入的循环依赖了。