『spring』Java面试题:Spring如何解决的循环依赖


『spring』Java面试题:Spring如何解决的循环依赖
文章图片
『spring』Java面试题:Spring如何解决的循环依赖
文章图片
『spring』Java面试题:Spring如何解决的循环依赖
Spring 是一个开源框架 , 为简化企业级应用开发而生 。 Spring 可以是使简单的 JavaBean 实现以前只有 EJB 才能实现的功能 。 Spring 是一个 IOC 和 AOP 容器框架 。
Spring如何解决的循环依赖 , 是近两年流行起来的一道Java面试题 。
其实笔者本人对这类框架源码题还是持一定的怀疑态度的 。
如果笔者作为面试官 , 可能会问一些诸如“如果注入的属性为null , 你会从哪几个方向去排查”这些场景题 。
那么既然写了这篇文章 , 闲话少说 , 发车看看Spring是如何解决的循环依赖 , 以及带大家看清循环依赖的本质是什么 。
正文
通常来说 , 如果问Spring内部如何解决循环依赖 , 一定是单默认的单例Bean中 , 属性互相引用的场景 。
比如几个Bean之间的互相引用:
甚至自己“循环”依赖自己:
先说明前提:原型(Prototype)的场景是不支持循环依赖的 , 通常会走到AbstractBeanFactory类中下面的判断 , 抛出异常 。
 if (isPrototypeCurrentlyInCreation(beanName)) {
  throw new BeanCurrentlyInCreationException(beanName);

原因很好理解 , 创建新的A时 , 发现要注入原型字段B , 又创建新的B发现要注入原型字段A...
这就套娃了 你猜是先StackOverflow还是OutOfMemory?
Spring怕你不好猜 , 就先抛出了BeanCurrentlyInCreationException
基于构造器的循环依赖 , 就更不用说了 , 官方文档都摊牌了 , 你想让构造器注入支持循环依赖 , 是不存在的 , 不如把代码改了 。
那么默认单例的属性注入场景 , Spring是如何支持循环依赖的?
Spring解决循环依赖
首先 , Spring内部维护了三个Map , 也就是我们通常说的三级缓存 。
笔者翻阅Spring文档倒是没有找到三级缓存的概念 , 可能也是本土为了方便理解的词汇 。
在Spring的DefaultSingletonBeanRegistry类中 , 你会赫然发现类上方挂着这三个Map:

  • singletonObjects 它是我们最熟悉的朋友 , 俗称“单例池”“容器” , 缓存创建完成单例Bean的地方 。
  • singletonFactories 映射创建Bean的原始工厂
  • earlySingletonObjects 映射Bean的早期引用 , 也就是说在这个Map里的Bean不是完整的 , 甚至还不能称之为“Bean” , 只是一个Instance.
后两个Map其实是“垫脚石”级别的 , 只是创建Bean的时候 , 用来借助了一下 , 创建完成就清掉了 。
所以笔者前文对“三级缓存”这个词有些迷惑 , 可能是因为注释都是以Cache of开头吧 。
为什么成为后两个Map为垫脚石 , 假设最终放在singletonObjects的Bean是你想要的一杯“凉白开” 。
那么Spring准备了两个杯子 , 即singletonFactories和earlySingletonObjects来回“倒腾”几番 , 把热水晾成“凉白开”放到singletonObjects中 。
闲话不说 , 都浓缩在图里 。
上面的是一张GIF , 如果你没看到可能还没加载出来 。 三秒一帧 , 不是你电脑卡 。
笔者画了17张图简化表述了Spring的主要步骤 , GIF上方即是刚才提到的三级缓存 , 下方展示是主要的几个方法 。