问题
spring使用三级缓存处理了循环依赖问题,并且第三级缓存中的对象工厂getObject()
的时候会牵扯到AOP,一般来说,getObject()
之后就会放到二级缓存中。
那么,为什么不能去掉三级缓存,每次都创建好代理版本放到二级缓存中完事?
AOP代理的两个时机
-
getObject()
的时候在SmartInstanitiaionAwareBeanPostProcessor
里面代理 - 初始化的时候使用
BeanPostProcessor
的回调代理
解释
getObject
会被调到的情况,只能是存在循环依赖的情况:
即,A工厂放入三级缓存,A准备属性注入B,B工厂放入三级缓存,B raw bean准备属性注入A,找到三级缓存中的A工厂,调用getObject
。
工厂其实包括两个东西,一个是raw bean,一个是lambda表达式,里面会有个特殊的postProcessor进行AOP代理。
如果不存在循环依赖,则A的属性赋值过程中都不会间接调用到getObject
方法,那么就往下走去到初始化阶段里面使用BeanPostProcessor
的回调进行动态代理。
- 不存在循环依赖的情况下,A在缓存中位置的变化是:先进入三级缓存,然后装配B,接着进入初始化阶段,之后直接进入一级缓存。
- 存在循环依赖的情况下,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 (注意,仅指自身属性赋值完整,属性的属性有没有赋值不管)
二级缓存:提前代理好了 & 属性未完全赋值
三级缓存:对象工厂,包含提前代理的逻辑
- 能否把二三级合并?每次都提前判断是否代理再放到二级缓存中?
可以,但是这样做彻底违背了Spring初衷,因为这样做的话,所有代理都提前到了初始化阶段前面做。 - 能否全部合并?每次都代理好放入一级缓存中?
可以,但是第一:违反了Spring属性赋值后代理的初衷,第二:第一级缓存中不仅存在属性赋值完成的,还存在属性未完整赋值的,属于是结构比较混乱,为了逻辑清晰,Spring选择把半成品和完成品分开。