Spring为什么要使用三级缓存?

1.首先需要明确, 什么是三级缓存, 以及配合工作的其他缓存.

所属位置: DefaultSingletonBeanRegistry, 如下图:

所属位置: DefaultSingletonBeanRegistry
一级缓存singletonObjects: 用来存储完整创建的Bean
二级缓存earlySingletonObjects: 用来存储未完全创建(实例化完成, 未完成属性注入,初始化)的Bean.
三级缓存singletonFactories: Bean工厂, 通过ObjectFactory获取当前不完整的Bean

2.具体指配合什么工作?-解决循环依赖

循环依赖: 顾明思义, 两个类之间相互依赖.
比如: A, B两个类, A中有B属性, B中有A属性, 即说明A,B之间相互依赖
class A{
    @Autowired
    private B b;
}
class B{
    @Autowired
    private A a;
}

3.Spring的三级缓存怎么解决的循环依赖?-基于Bean的生命周期
简略的Bean生命周期可概括为4个阶段
1)实例化阶段: 通过反射判断类对应的构造函数,创建相应的实例
2)属性注入: 通过IOC对Bean内部的属性赋值.
3)初始化: 执行指定需要的初始化方法, 执行init-method, @PostConstruct等需要看实际场景
4)使用后销毁: 使用完销毁阶段, 随容器结束执行相应的诸如PreDestroy的方法

解决循环依赖的方法, 通过三级缓存, 提前暴露了未完全创建的的Bean, 使Bean生成得以完整的进行下去
比如模拟一个A,B类循环依赖场景
1)Spring首先尝试从一级缓存中找到A类, 若不存在去二级缓存查找, 不存在再去三级缓存查找.
2)如果缓存查找到则返回, 否则通过AbstractAutowireCapableBeanFactory的createBean方法创建Bean
3)实例化完A后, 发现其属性中有B类, 此时无法继续进行, 将当前A类标记为正在创建中,移动到正在创建的Map, 并加入到三级缓存中.
4)用同样的方法尝试寻找B, 发现B类也不存在在缓存中, 因此也要创建B
5)实例化完B后开始进行属性注入, 发现其属性中有A类, 此时可以在三级缓存中获取到正在创建中的A类, 完成属性注入,进而初始化完成B的创建.
6)同时A类需要的B类创建完成, A类也可以完成属性注入, 进而完成创建, 到此循环依赖的两个类A,B均完成了创建.

4.解决循环依赖的前提, 一些无法解决的循环依赖的场景.
1)循环依赖的对象必须都是单例, 因为多例每次都是新生成的对象, 所以无法放入缓存中.
2)循环依赖的对象最好都是setter注入, 至少通过包名+类型按照字母序的第一个类不能是构造器注入,原因是如果是构造器注入, 构造函数会在实例化阶段通过反射被调用, 而将创建中的Bean放入三级缓存是在实例化阶段完成时才进行的,所以此时无法解决循环依赖了.

5.为什么需要三级缓存, 只用二级缓存是否可以.
如果产生循环依赖的对象, 都是原本对象, 不需要生成代理对象, 即不需要进行AOP, 则使用二级缓存, 完全可以.
但是如果其中有对象需要AOP, 我们知道在Bean生命周期中, 根据对象生成代理对象是在初始化完成后, 通过拓展接口postProcessAfterInitialization方法进行的
而循环依赖的解决位置还未到达初始化这一步骤, 这就会导致一个问题, 如上方描述的解决A,B两个对象的循环依赖
1)A,B中 如果A要被AOP, 那A被放入缓存进入到B的属性注入时为实例化的原对象.
2)完成B的创建后, A在过了属性注入到达初始化方法后进行AOP生成代理对象, 最后完成创建并放入到容器中.
这里面导致的问题就是B中的A对象, 与容器中的A对象并不是同一个, 一个是实例化的原对象, 一个是AOP后的代理对象, 这显然是有问题的.

所以使用三级缓存的意义就在于此, 我们可以观察源码, 在生命周期源码的允许暴露的位置存在该代码:


后方的钩子函数, 在当前创建类被引用时会触发调用, 再进入一层
发现通过SmartInstantiationAwarePostProcessor后置处理器调用了一个getEarlyBeanReference的方法,而该接口有两个实现类
其中之一为InstantationAwarePostProcessorAdapter 该接口返回的是当前bean
另外一个是AbstractAutoProxyCreator该接口传入了当前bean和beanName 通过调用WrapIfNecessary方法生成了代理对象,并加入到了earlyProxyReferences中然后在postProcessAfterInitialization判断了earlyProxyReferences是否存在当前bean 如果存在直接返回了相应的bean, 如下图AbstractAutoProxyCreator:

有些同学可能会有疑问,上述逻辑与AOP有何关系, 根本原因在于AbstractAutoProxyCreator这个类, AOP的顶层接口AspectJAwareAdvisorAutoProxyCreator的父类也是该类 

至此可以确定, 为什么要存在并且使用三级缓存来解决循环依赖了~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值