SpringIOC之循环依赖问题思考

BeanFactory 是如何处理循环依赖?

前言,下面的“循环依赖”换成“循环依赖注入”比较合适,在 Spring 中通过 depends-on 配置的依赖对象如果出现循环依赖会抛出异常

说明:这里的循环依赖指的是单例模式下的 Bean 字段注入时出现的循环依赖。构造器注入对于 Spring 无法自动解决(应该考虑代码设计是否有问题),可通过延迟初始化来处理(lazy注解)。Spring 只解决单例模式下的循环依赖。

在 Spring 底层 IoC 容器 BeanFactory 中处理循环依赖的方法主要借助于以下 3 个 Map 集合:

  1. singletonObjects(一级 Map),里面保存了所有已经初始化好的单例 Bean,也就是会保存 Spring IoC 容器中所有单例的 Spring Bean;

  1. earlySingletonObjects(二级 Map),里面会保存从 三级 Map 获取到的正在初始化的 Bean

  1. singletonFactories(三级 Map),里面保存了正在初始化的 Bean 对应的 ObjectFactory 实现类,调用其 getObject() 方法返回正在初始化的 Bean 对象(仅实例化还没完全初始化好),如果存在则将获取到的 Bean 对象并保存至 二级 Map,同时从当前 三级 Map 移除该 ObjectFactory 实现类。

当通过 getBean 依赖查找时会首先依次从上面三个 Map 获取,存在则返回,不存在则进行初始化,这三个 Map 是处理循环依赖的关键。

例如两个 Bean 出现循环依赖,A 依赖 B,B 依赖 A;当我们去依赖查找 A,在实例化后初始化前会先生成一个 ObjectFactory 对象(可获取当前正在初始化 A)保存在上面的 singletonFactories 中,初始化的过程需注入 B;接下来去查找 B,初始 B 的时候又要去注入 A,又去查找 A ,由于可以通过 singletonFactories 直接拿到正在初始化的 A,那么就可以完成 B 的初始化,最后也完成 A 的初始化,这样就避免出现循环依赖。

问题一:为什么需要上面的 二级 Map
因为通过 三级 Map获取 Bean 会有相关 SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(..) 的处理,避免重复处理,处理后返回的可能是一个代理对象
例如在循环依赖中一个 Bean 可能被多个 Bean 依赖, A -> B(也依赖 A) -> C -> A,当你获取 A 这个 Bean 时,后续 B 和 C 都要注入 A,没有上面的 二级 Map的话, 三级 Map 保存的 ObjectFactory 实现类会被调用两次,会重复处理,可能出现问题,这样做在性能上也有所提升
问题二:为什么不直接调用这个 ObjectFactory#getObject() 方法放入 二级Map 中,而需要上面的 三级 Map
对于不涉及到 AOP 的 Bean 确实可以不需要 singletonFactories(三级 Map),但是 Spring AOP 就是 Spring 体系中的一员,如果没有singletonFactories(三级 Map),意味着 Bean 在实例化后就要完成 AOP 代理,这样违背了 Spring 的设计原则。Spring 是通过 AnnotationAwareAspectJAutoProxyCreator 这个后置处理器在完全创建好 Bean 后来完成 AOP 代理,而不是在实例化后就立马进行 AOP 代理。如果出现了循环依赖,那没有办法,只有给 Bean 先创建代理对象,但是在没有出现循环依赖的情况下,设计之初就是让 Bean 在完全创建好后才完成 AOP 代理。
提示:AnnotationAwareAspectJAutoProxyCreator 是一个 SmartInstantiationAwareBeanPostProcessor 后置处理器,在它的 getEarlyBeanReference(..) 方法中可以创建代理对象。所以说对于上面的 问题二,如果出现了循环依赖,如果是一个 AOP 代理对象,那只能给 Bean 先创建代理对象,设计之初就是让 Bean 在完全创建好后才完成 AOP 代理。
为什么 Spring 的设计是让 Bean 在完全创建好后才完成 AOP 代理?
因为创建的代理对象需要关联目标对象,在拦截处理的过程中需要根据目标对象执行被拦截的方法,所以这个目标对象最好是一个“成熟态”,而不是仅实例化还未初始化的一个对象。

对应源码位置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder_Boy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值