Spring循环依赖是什么?怎么解决

前言

Spring如何解决的循环依赖,是近两年流行起来的一道Java面试题。

其实笔者本人对这类框架源码题还是持一定的怀疑态度的。

如果笔者作为面试官,可能会问一些诸如“如果注入的属性为null,你会从哪几个方向去排T u d z s , Q查”这些场景题

那么既然写S % 8了这篇文章,闲话少说,发车看看Spring是如何解决的循环依赖,以及带大家看清循环依赖的本质是什么

正文

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

比如几个Beany ! Y ( M之间的互相引用:C Y I #

Spring循环依赖是什么?怎么解决

甚至自己“循环”依| H H U T _ p C B赖自己:

Spring循环依赖是什么?怎么解决

先说明前提:原型(Prototype)的场景是不支持循环依赖的,通常会走到AbstractBe* ) g ) = kanFactory类中下面的判断,抛出异常。

Spring循环依赖是什么?怎么解决

原因很好理解,创建新的A时,发现要注入原型字段B,又创建新的B发现要注入原型字段A...

这就套娃了, 你猜是先Stl / g fackOverflow还是OutOfMemory

Spring怕你不好猜,就先抛出了BeanCurrentlyInCreationException

Spring循环依赖是什么?怎么解决

基于构造器g V qm T m |循环依[ b ,赖,就更不用说了,官方文档都摊牌了,你想让构造器注入支持循环依赖,是不存在的,不如把代码改了。

那么默认单例的属性注V i d x入场q V @ j m Q T景,Spring是如何支持循环依赖的?

Spu k y Zring解决循环依赖

首先,Spring内部维护了三个Map,也就是我们通常说的三级缓存

笔者翻阅Spring文档倒是没有找到三级缓存的概念,可能也是本土为了方便理解的词汇。

在Spring的DefaultSingle& i Q R ; N ztonBeanRegistry类中,你会赫然发现类上方挂着这三个Map:

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

后两个Map其实是“垫脚石”级别的, f v只是创建Bean的时候,用来借助了一下,创建完成就清掉了。

Q K W | ) & / 6 B以笔者前文对“三级缓存”这个词有些迷惑,可能是因为注释都是以Cache of开/ 6 HZ k c ]吧。

为什么成为后两个Map为垫脚石,假设最终放在singletonObjY g 8 ~ S T 2 Rects的Bean是你想要的一杯“凉白开”。

那么Spring准备了两个杯子,即singletonFactJ g G 0 c zories和earlySingletonObjects来回“倒腾”几番,把热水晾成“凉白开”放到singletonObjects中。

闲话不说,都浓缩在图里。

Spring循环依赖是什么?怎么解决

上面的是一张GID e U @ ~ + 8F,如果你没看到可能还没加载出来。三秒一帧,不是你电脑卡。

笔者画了17张图简化表述了Spring的主要步骤,GIFk y ? s @ 上方即是刚才提到的三级缓存,E f $ )下方展示/ _ n 3 , y V `主要的几个方法。

当然了# I 6 ?,这个地步你肯定要结合Spring源码来看,要不肯定看不懂。

如果你只是想大概了解,或者面试,可以先记住笔者上文提到的“三级缓存”,以及下文即将要说的本质。

循环依赖的本m D q A p 0

上文了S = t解完Spring如何处理循环依赖之后,让我们跳出“阅读源码”的思维,假设让你实现一个有以下特点的功能,你会怎么做?

  • 将指定的一些类实例为单例
  • 类中的字段O 6 8 2 1 &也都实例为单例
  • 支持循环* r ;依赖

举个例子,假设有类A:

public class A {
    private&` U C )nbsp;B b;
}

类B:

public&u X & ` + / ? z Tnbsp;class B {
   &nbsO [ 4 w x M tp;private A Q R H + K @ r `a;
}

说白了让你模仿Spring假装AB是被@Component修饰,并且类中的字段假装是@AutowI f +ired修饰的,处理完放到Map中。

其实非常简单,笔者写了一份粗糙的代码,可供参考:

Spring循环依赖是什么?怎么解决

这段代码的效果,其实就是处理了循环6 w ~依赖,并且处理完成后,cacheMap中放的就是完整的“Bean”了

Spring循环依赖是什么?怎么解决

这就是“循环依赖”的本质,而不是“Spring如何解决循环依赖”。

之所以要举这个例子,是发现一小部分盆友陷入了“阅读源码的泥潭”,而忘记了问题的本质。

为了看源码而看源码,结果一直看不B U . x j懂,却忘了本质是什么。

如果真看不懂,不如J : s K @ H ^先写出基础版本,逆推Spring为什么要这么实现,可能效果会更好。

whai L & u E qt?问! m 0 ! + Z x T题的本质居然是two sum!

看完笔者刚才的代码有没有似曾相识?没错,和twoO n [ ` / sum的解题是类似的。

不知道two sum是什么梗的,笔_ J w T U Y a t者和你介绍一下:

two sumw ? + F O W刷题网站leetcode序号为1的题,也就是大多人的算法入门的M M K 4 /第一题。

常常被人调侃,有算法面的公司,被面试官钦定了,合的来。那就来一道two sum走走过场。

问题内容是:给定一个数组,给定Y i g t 4 Ov 7 b f b @ $ }数字。返回数组中可以相加得到指定数字的两个索引

比如:给定nuo ~ g H M { H w ims = [2, 7, 11, 15], target = 9那么要返回 [0, 1],因为2 + 7 = 9

这道题的优解是,一次遍历+HashMap:

Spring循环依赖是什么?怎么解决

先去Map中找需要的数字,没有就将当前的数字保存在Map中,如果找到需要的数字,则一起返回。

和笔者上面的代码是不是一样?

先去缓存里找Bean,没有则实例化当前的Bean放到Map,如果有需要依赖当前Bean的,就能从Map取到。

结尾

如果你是上文笔者提到的A O 7 O @ +陷入阅读源码的泥潭”的读者,上文应该可以帮助到你。

可能还有[ | b m盆友有疑问,为什么一道“two-sum”,Spring处理的如此复杂?这个想想Spring支持多少功能就知道了,各4 ? L O { y (种实例方式..各种注入方式..各种Bean的加载,校验..各种callback,aop处理等等u 4 C s Y W 3..

Spring可不$ e x 2 Q只有依赖注入,同样Java也不仅是Spring。如果我u b F a ! ! i们陷入了某个“牛角尖”,不妨跳出来看看,可能会更佳清晰哦。

上一篇

常吃蒲公英对健康有7大好处,但这3类人要注意

下一篇

本以为谭松韵只走可爱风,没想到穿上风衣玩酷帅却惊艳了!

评论已经被关闭。

插入图片
返回顶部