先说下bean的初始化过程
1、bean实例化
2、填充属性
3、执行后置处理器
4、初始化完成放入spring容器
spring解决循环依赖 依靠三级缓存
先看下三级缓存出现循环依赖的情况
1、初始化beanA 这里有没有代理都没有关系 直接把ObjectFactory放入到三级缓存 获取beanB
2、获取一级缓存 发现没有 继续
3、获取二级缓存 发现没有 继续
4、获取三级缓存 发现没有 需要初始化beanB
5、初始化B 然后把对应的ObjectFactory放入到三级缓存 然后填充属性 发现依赖A
6、获取一级缓存 发现没有 继续
7、获取二级缓存 发现没有 继续
8、获取三级缓存 发现有 则调用对应的ObjectFactory.getObject生成对象 如果有代理类 则获取A的代理类 然后放到二级缓存 删除三级缓存A的ObjectFactory 后面其他bean也依赖A的时候 直接在二级缓存就会获取到 不需要在调用ObjectFactory.getObject生成对象
9、获取到代理类A以后 B就可以填充属性完成
10、之后B会执行后置处理器 在执行到代理类部分的时候 会判断已经生成过代理类 所以直接返回不会继续生成代理类(初始化以后就放入到一级缓存 后续获取beanB的时候也就直接在一级缓存里获取 不会从三级缓存中获取然后生成代理类) 也就是在整个bean的生命周期里 只会生成一次代理类 初始化完成
10、B初始化完成以后放入一级缓存删除二级三级缓存
为什么不用二级缓存
需要从多个方面看
1、bean默认和一般使用都是为了单例
2、spring在设计的时候 是希望aop生成代理类的时候 是在bean的装填完属性以后执行后置处理器生成代理类
3、ObjectFactory.getObject每次返回的都是新的对象
上面的三个条件 如果只用二级缓存是没办法实现的 三级缓存其实也是有部分妥协
如果二级缓存的时候只能下面的方式
1、实例化beanA 如果有代理 是则调用ObjectFactory.getObject生成代理类 放到二级缓存(如果是只把ObjectFactory放进二级缓存 当获取的时候 再通过ObjectFactory.getObject获取的时候会导致每次都new一个新的对象 代理类也是每次都生成一个新的代理类 和单例不符而且代理类部分会报错 ) 获取属性beanB
2、获取一级缓存 发现没有 继续
3、获取二级缓存 发现没有 需要初始化beanB
4、实例化beanB 如果有代理 则调用ObjectFactory.getObject生成代理类放到二级缓存
可以看到 只要有代理 都会在实例化的时候就生成代理 这个就和希望aop生成代理类的时候 是在bean的装填完属性以后执行后置处理器生成代理类设计原则违背了 都是在实例化的时候就要生成代理类
\
当没有循环依赖的时候
5、实例化beanB 然后把对应的ObjectFactory放入到三级缓存 然后填充属性 完成初始化
6、beanA获取到beanB以后就可以填充属性完成 初始化成功
7、beanA如果有代理则会生成代理类 然后删除三级缓存放到一级缓存
可以看到 没有循环依赖的时候 bean如果有代理 则是在bean初始化完成以后才会生成代理类 符合aop和bean生命周期的约束 不过如果循环依赖 依然还是会导致bean初始化中的时候 出现循环依赖 不过比所有代理类都在初始化中的的时候就生成代理类 更加符合aop和bean生命周期的约束设计
这是网上看到的评论 下面那个感觉更符合我的想法
题外话
如果使用@async的话 出现循环依赖哪怕用了三级缓存也会有问题 就核心原因是在执行ObjectFactory.getObject的时候 只能获取代理类 而执行后置处理器的时候 执行到@async的后置处理器会生成一个async的代理类 和ObjectFactory.getObject的时候生成的代理类不一致 就会导致spring报错 除非在ObjectFactory.getObject的时候就返回async的代理类 不过不知道为什么写async的作者不这么做 理论上应该是可以实现的和aop代理类同样在getEarlyBeanReference的时候生成 如果要解决这个问题 目前spring是可以通过懒加载注解来解决 可能async的作者觉得用懒加载更合适 直接不打破代理都是在后置处理器执行的设计
借鉴