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

问题

spring使用三级缓存处理了循环依赖问题,并且第三级缓存中的对象工厂getObject()的时候会牵扯到AOP,一般来说,getObject()之后就会放到二级缓存中。
那么,为什么不能去掉三级缓存,每次都创建好代理版本放到二级缓存中完事?

AOP代理的两个时机

  1. getObject()的时候在SmartInstanitiaionAwareBeanPostProcessor里面代理
  2. 初始化的时候使用BeanPostProcessor的回调代理

解释

getObject会被调到的情况,只能是存在循环依赖的情况
即,A工厂放入三级缓存,A准备属性注入B,B工厂放入三级缓存,B raw bean准备属性注入A,找到三级缓存中的A工厂,调用getObject

工厂其实包括两个东西,一个是raw bean,一个是lambda表达式,里面会有个特殊的postProcessor进行AOP代理。

如果不存在循环依赖,则A的属性赋值过程中都不会间接调用到getObject方法,那么就往下走去到初始化阶段里面使用BeanPostProcessor的回调进行动态代理。

  1. 不存在循环依赖的情况下,A在缓存中位置的变化是:先进入三级缓存,然后装配B,接着进入初始化阶段,之后直接进入一级缓存。
  2. 存在循环依赖的情况下,A在缓存中位置的变化是:先进入三级缓存,然后装配B,接着被B调用工厂方法,提前AOP,进入二级缓存,之后再装配一级缓存中的B,自身完整赋值好了,就进入一级缓存。

不知道从哪里看见的,说是Spring设计初衷就是要Bean完全属性赋值完成之后再进行AOP(对应生命周期是初始化阶段),因此,初始化阶段会有BeanPostProcessor的回调方法进行AOP代理,此时的target都是赋值完整的target。

但是在存在循环依赖的时候,这个设计初衷就被打破了。

举个例子,仅考虑2个对象AB的相互依赖,AB都有代理逻辑。
实例化后的『A原始Bean』装配B => 实例化后的『B原始Bean』装配A,那么这个时候装配的A必须进行代理,否则装配的就是原始A了!所以B调用了A工厂的getObject提前进行代理,然后注入到B中,B就可以顺利属性赋值完成,然后进入初始化阶段,B在初始化阶段进行AOP。

既然A提前代理了,当A到初始化阶段再次碰到代理逻辑怎么办?
实际上会判断下是否已经提前代理过了,如果代理过了,初始化阶段的代理逻辑就不做了,用之前的代理Bean作为结果。

总结

一级缓存:代理过 & 属性赋值 完成的Bean (注意,仅指自身属性赋值完整,属性的属性有没有赋值不管)
二级缓存:提前代理好了 & 属性未完全赋值
三级缓存:对象工厂,包含提前代理的逻辑

  1. 能否把二三级合并?每次都提前判断是否代理再放到二级缓存中?
    可以,但是这样做彻底违背了Spring初衷,因为这样做的话,所有代理都提前到了初始化阶段前面做。
  2. 能否全部合并?每次都代理好放入一级缓存中?
    可以,但是第一:违反了Spring属性赋值后代理的初衷,第二:第一级缓存中不仅存在属性赋值完成的,还存在属性未完整赋值的,属于是结构比较混乱,为了逻辑清晰,Spring选择把半成品和完成品分开。

猜你喜欢

转载自blog.csdn.net/weixin_43696693/article/details/130118675