Spring Bean创建过程
循环依赖的发生
循环依赖就发生在bean创建流程中依赖注入一步,这一步主要是对bean的依赖属性进行填充,对@Value @Autowired @Resource注解标注的属性注入对象引用。
循环依赖就是发生了beanA对象中有一个属性b需要注入beanB对象,但是同时beanB对象中也有一个属性a需要beanA注入。如下图:
这样就会发生这样的事:
- 创建A的bean对象时,当执行到依赖注入这一步时发现需要B的bean对象注入。
- 在容器中寻找B对象,此时B对象没有创建,找不到,于是停下A的创建工作,先创建B。
- 创建B的bean对象,当走到依赖注入这一步时发现需要A的bean对象注入,但是A的bean对象还没有创建完成,于是陷入了死循环。
Spring解决循环依赖
提前曝光机制:把还没有完全建立好的bean对象暴露出去。
三级缓存:建立三个Map结构,分别存放初始化完全的bean对象,未完成创建的bean对象、ObjectFactory对象工厂。
二级缓存
图中即二级缓存的流程图,将未创建完成的对象放到二级缓存中,提前暴露出去,这样就可以避免循环依赖。
缺陷:bean的创建过程中有一个BeanPostProcessor#after,我们的代理对象就是在一步创建的,当我们使用的是二级缓存解决循环依赖时,二级缓存中存储的是在依赖注入阶段的半成品bean对象A,在这个阶段我们创建的半成品bean只是对象并不是代理对象,如果把这个对象注入到B的bean对象中,那么B对象调用A对象方法时将无法使用代理对象,那么AOP、事务等依靠代理对象的机制将失效。
三级缓存
三级缓存将解决二级缓存无法处理的代理对象问题
如图所示
这次我们不是把卡在依赖注入阶段的半成品A放入二级缓存,而是将A的ObjectFactory对象工厂对象放入三级缓存,当我们走到实例化B时需要A的注入,此时会依次从一二三级缓存中找,最终找到三级缓存中的A的ObjectFactory对象工厂,这个对象工厂会对A进行判断,如果A没有AOP就返回一个半成品A实例对象,如果有就包装一个A的代理对象返回。
总结
总结下Spring解决循环依赖的思路:在创建单例bean时,会把该bean的工厂函数的匿名类对象放入三级缓存中的singletonFactories中;然后在填充属性时,如果出现循环依赖依赖本 bean,必然执行之前放入的工厂函数的匿名实现,如果该bean无需 AOP的话,工厂函数返回的就是原bean对象;如果该bean有 AOP 的话,也有可能是被某些BBP处理AOP 之后的代理对象,会放入二级缓存中的earlySingletonObjects中;接着bean开始初始化,如果该bean无需 AOP的话,结果返回的原来创建的bean对象;如果该bean有 AOP 的话,检查AOP织入逻辑是否已经在提前曝光时已经执行了,如果已经执行AOP则返回提前曝光的代理bean对象;如果AOP织入逻辑未执行过,则进行后续的 BeanPostProcessor后置处理器进行AOP织入,生成AOP代理bean对象,并返回。最后对于提前曝光的单例,就会去检查初始化后的bean对象与二级缓存中提前曝光的bean是不是同一个对象,只有不是的情况下才可能抛出异常。