0%

Spring 解决循环依赖的问题

前言

  • 通常来说,如果问Spring内部如何解决循环依赖,一定是单默认的单例 Bean 中,属性互相引用的场景

  • 原型( Prototype )的场景是不支持循环依赖的,通常会走到 AbstractBeanFactory类中下面的判断,抛出异常

  • 构造器的循环依赖,就更不用说了,官方文档都摊牌了,你想让构造器注入支持循环依赖,是不存在的,不如把代码改了。

Spring解决循环依赖

在 Spring 的 DefaultSingletonBeanRegistry 类中,维护了三个 Map ,也就是我们通常说的三级缓存(三级缓存并非官方文档描述,只是一种对代码的理解称呼)

个人认为并不能理解为缓存,缓存的最终目的是为了解决性能,而这三个map的目的更多的是标识一个bean的创建状态。

singletonObjects 表示bean已经完整的被创建

singletonFactories 在单例工厂中的表示bean正在被创建

earlySingletonObjects 在early中的表示已经被创建但完整

  • singletonObjects 它是我们最熟悉的朋友,俗称“单例池”“容器”,缓存创建完成单例Bean的地方。
  • singletonFactories 映射创建Bean的原始工厂
  • earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.

singletonFactoriesearlySingletonObjects 只是临时保存数据的集合,bean创建完毕后,就会清除里面的内容

循环依赖的本质

  1. 利用缓存识别已经遍历过的节点;
  2. 利用Java引用,先提前设置对象地址,后完善bean;

图解流程

1614048539636

面试题

Spring是如何解决的循环依赖?

  • Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories)。
  • 当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。
  • 当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。
  • 当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?

  • 如果要使用二级缓存解决循环依赖,意味着所有 Bean 在实例化后就要完成 AOP 代理,这样违背了 Spring 设计的原则,Spring 在设计之初就是通过 AnnotationAwareAspectJAutoProxyCreator 这个后置处理器来在 Bean 生命周期的最后一步来完成 AOP 代理,而不是在实例化后就立马进行 AOP 代理。

参考:

https://juejin.cn/post/6844904122160775176

https://zhuanlan.zhihu.com/p/157611040


----------- 本文结束啦感谢您阅读 -----------