Spring Bean 循环依赖

本文详细解释了Spring框架中Bean创建时如何处理循环依赖问题,通过提前曝光机制和三级缓存(包括二级缓存的局限和三级缓存的改进),确保AOP和事务等功能正常工作。
摘要由CSDN通过智能技术生成

Spring Bean创建过程

循环依赖的发生

循环依赖就发生在bean创建流程中依赖注入一步,这一步主要是对bean的依赖属性进行填充,对@Value @Autowired @Resource注解标注的属性注入对象引用。

循环依赖就是发生了beanA对象中有一个属性b需要注入beanB对象,但是同时beanB对象中也有一个属性a需要beanA注入。如下图:

这样就会发生这样的事:

  1. 创建A的bean对象时,当执行到依赖注入这一步时发现需要B的bean对象注入。
  2. 在容器中寻找B对象,此时B对象没有创建,找不到,于是停下A的创建工作,先创建B。
  3. 创建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是不是同一个对象,只有不是的情况下才可能抛出异常。

相关链接:一文详解Spring Bean循环依赖-阿里云开发者社区 (aliyun.com)

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值