浅聊Spring中的循环依赖和三级缓存

写在前面 : Spring的循环依赖 思维导图
浅聊Spring中的循环依赖和三级缓存_第1张图片

1. 什么是循环依赖?

简单说就是 A对象依赖于B对象的同时B也依赖于A, 如下图
浅聊Spring中的循环依赖和三级缓存_第2张图片
代码举例:

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

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

浅聊Spring中的循环依赖和三级缓存_第3张图片
浅聊Spring中的循环依赖和三级缓存_第4张图片

2. 三级缓存解决循环依赖

浅聊Spring中的循环依赖和三级缓存_第5张图片
浅聊Spring中的循环依赖和三级缓存_第6张图片
浅聊Spring中的循环依赖和三级缓存_第7张图片
浅聊Spring中的循环依赖和三级缓存_第8张图片
以ABC三个实例循环依赖为例, 整个流程大致如下:

  1. 首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),放到三级缓存中,此时k=BeanName,v=ObjectFactory , 在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来;

  2. 然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖 C,C 也没有被创建出来;

  3. 这个时候 C 又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A)。这个时候由于 A 已经添加至缓存中(一般都是添加至三级缓存 singletonFactories),通过ObjectFactory 提前曝光,所以可以通过 ObjectFactory#getObject() 方法来拿到 A 对象。C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中;

  4. 回到 B,B 也可以拿到 C 对象,完成初始化,A 可以顺利拿到 B 完成初始化。到这里整个链路就已经完成了初始化过程了。

关键字:三级缓存,提前曝光。

第三级缓存 考虑 代理 key=BeanName value=ObjectFactory
第二级缓存 考虑 性能 key=BeanName value=Bean(此时bean还没做完属性注入相关工作)

3. 构造方法出现循环依赖的解决

浅聊Spring中的循环依赖和三级缓存_第9张图片

4. 总结一下三级缓存 :

4.1.singletonObjects:单例池 , 一级缓存
缓存的是某个 beanName 对应的经历了完整的Bean生命周期的Bean

4.2 earlySingletonObjects:二级缓存
缓存的是未经过完整的Bean生命周期的Bean,如果出现了循环依赖 , 那么就会提前把这个暂时未经过Bean生命周期的Bean放入这个缓存 , 如果这个Bean需要经过AOP , 那么就会把代理对象放入这个缓存 , 就算是经过了AOP , 那么这个代理对象代理的真实对象也是是未完成生命周期的 , 所以我称它为 半成品 Bean , 这个缓存可以理解为存放的是未经过完整的Bean生命周期的Bean (还未属性注入和后续BeanPostProcessor等生命周期)

4.3 singletonFactories:三级缓存
缓存的是ObjectFactory , 也就是一个Lambda表达式 , 这个表示在存入的时候不会执行 , 在get的时候会执行,在每个Bean生成一个原始对象的时候 , 都会基于这个原始对象暴露一个lambda表达式 , 然后放到这个缓存 , 这个lambda可能用到 , 也可能用不到 , 如果当前Bean没有出现循环依赖的情况 , 那么这个表达式就没用 , 按照自己的生命周期正常执行 , 执行完之后把这个对象放入单例池 , 如果出现了循环依赖(当前创建的Bean被其他的Bean依赖了) , 那么就会把这个表达式取出来 , 然后执行得到一个对象 , 并把得到的缓存放入二级缓存 , (如果当前Bean需要AOP , 那么执行完lambda之后得到的就是一个代理对象 ,如果无需AOP , 那就是原始对象)

4.4 earlyProxyReferences
其实还有这个缓存 , 用来记录某个原始对象是否进行了AOP

你可能感兴趣的:(JAVA,缓存,spring,java)