|Spring循环依赖,我本来都不想写的,但网上好多错误观点

|Spring循环依赖,我本来都不想写的,但网上好多错误观点

文章图片

|Spring循环依赖,我本来都不想写的,但网上好多错误观点

文章图片

|Spring循环依赖,我本来都不想写的,但网上好多错误观点

文章图片


今天我们来盘 Spring 的经典面试题循环依赖 。
这的面试题很经典 , 网上相应的文章很多 , 但是我还是想写一下 , 因为有些文章的观点在我看来是错的 。

  • 比如有构造器就无法解决循环依赖?
  • 一定要三级缓存才能解决循环依赖?
  • 到底为什么要三级缓存?
好了 , 话不多少 , 我们来盘一盘 。
什么是循环依赖很简单 , 看下方的代码就知晓了
@Servicepublic class A {    @Autowired
   private B b;
@Servicepublic class B {    @Autowired
   private A a;
//或者自己依赖自己@Servicepublic class A {    @Autowired
   private A a;


上面这两种方式都是循环依赖 , 应该很好理解 , 当然也可以是三个 Bean 甚至更多的 Bean 相互依赖 , 原理都是一样的 , 今天我们主要分析两个 Bean 的依赖 。

这种循环依赖可能会产生问题 , 例如 A 要依赖 B , 发现 B 还没创建 。
于是开始创建 B, 创建的过程发现 B 要依赖 A ,而 A 还没创建好呀 , 因为它要等 B 创建好 。
就这样 它们俩就搁这卡 bug 了。
Spring 如何解决循环依赖上面这种循环依赖在实际场景中是会出现的 , 所以 Spring 需要解决这个问题 , 那如何解决呢?
关键就是 提前暴露未完全创建完毕的 Bean。
在 Spring 中 , 只有同时满足以下两点才能解决循环依赖的问题:
  1. 依赖的 Bean 必须都是单例
  2. 依赖注入的方式 , 必须 不全是 构造器注入 , 且 beanName 字母序在前的不能是构造器注入
为什么必须都是单例如果从源码来看的话 , 循环依赖的 Bean 是原型模式 , 会直接抛错:

所以 Spring 只支持单例的循环依赖 ,但是为什么呢 ?
按照理解 , 如果两个 Bean 都是原型模式的话 。
那么创建 A1 需要创建一个 B1 。
创建 B1 的时候要创建一个 A2 。
创建 A2 又要创建一个 B2 。
创建 B2 又要创建一个 A3 。
创建 A3 又要创建一个 B3.....
就又卡 BUG 了 , 是吧 , 因为原型模式都需要创建新的对象 , 不能跟用以前的对象 。
如果是单例的话 , 创建 A 需要创建 B , 而创建的 B 需要的是之前的个 A ,不然就不叫单例了 , 对吧?
也是基于这点 ,Spring 就能操作操作了 。
【|Spring循环依赖,我本来都不想写的,但网上好多错误观点】具体做法就是:先创建 A , 此时的 A 是不完整的(没有注入 B) , 用个 map 保存这个不完整的 A , 再创建 B, B 需要 A 。
所以从那个 map 得到“不完整”的 A , 此时的 B 就完整了 , 然后 A 就可以注入 B , 然后 A 就完整了 , B 也完整了 , 且它们是相互依赖的 。

读起来好像有点绕 , 但是逻辑其实很清晰 。
为什么不能全是构造器注入在 Spring 中创建 Bean 分三步:
  1. 实例化 , createBeanInstance , 就是 new 了个对象
  2. 属性注入 , populateBean ,就是 set 一些属性值
  3. 初始化 , initializeBean , 执行一些 aware 接口中的方法 , initMethod , AOP代理等