Spring是怎么解决循环依赖的?

什么是循环依赖

循坏依赖就是字面意思,A 依赖了 B,B 同时也依赖了 A。
Spring是怎么解决循环依赖的?_第1张图片
如下所示

@Component
public class A {
    // A中注入了B
	@Autowired
	private B b;
}

@Component
public class B {
    // B中也注入了A
	@Autowired
	private A a;

又或者是下面这种

// 自己依赖自己
@Component
public class A {
    // A中注入了A
	@Autowired
	private A a;
}

解决循环依赖

前提

解决循环依赖有两个前置条件:

  1. 出现循环依赖的 Bean 必须是单例
  2. 依赖注入的方式不全是构造器方式注入。

解决

基于上面的问题,我们知道Bean的生命周期,本质上解决循环依赖的问题就是三级缓存,通过三级缓存提前拿到未初始化完全的对象。

第一级缓存:用来保存实例化、初始化都完成的对象。
第二级缓存:用来保存实例化完成,但是未初始化完成的对象。
第三级缓存:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象。
Spring是怎么解决循环依赖的?_第2张图片
A 对象创建过程:

1、创建对象 A,实例化的时候把 A 对象工厂放入三级缓存
Spring是怎么解决循环依赖的?_第3张图片
2、A 注入属性时,发现依赖 B,转而去实例化 B。
3、同样创建对象 B,注入属性时发现依赖 A,一次从一级到三级缓存查询 A, 从三级缓存通过对象工厂拿到 A,把 A 放入二级缓存,同时删除三级缓存中的 A,此时,B 已经实例化完并且初始化完成,把 B 放入一级缓存。
Spring是怎么解决循环依赖的?_第4张图片
4、接着继续创建 A,顺利从一级缓存拿到实例化且初始化完成的 B 对象,A 对象创建也完成,删除二级缓存中的 A,同时把A 放入一级缓存。
5、最后,一级缓存中保存着实例化、初始化都完成的 A、B 对象。
Spring是怎么解决循环依赖的?_第5张图片
因此,由于把实例化和初始化的流程分开了,所以如果都是用构造器的话,就没法分离这个操作,所以都是构造器的话就无法解决循环依赖的问题了。

为什么要三级缓存?二级不行吗?

不可以,主要是为了生成代理对象。
因为三级缓存中放的是生成具体对象的匿名内部类,他可以生成代理对象,也可以是普通的实例对象。
使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。

假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,BeanPostProcessor去生成代理对象之后,覆盖掉二级缓存中的普通Bean对象,那么多线程环境下可能取到的对象就不一致了。
在这里插入图片描述

你可能感兴趣的:(spring,java,后端)