本文共 1142 字,大约阅读时间需要 3 分钟。
Spring如何解决循环依赖,是近年来热门的Java面试题之一。笔者对这类框架源码题持有一定的怀疑态度,但作为面试官可能会出一些场景题,如“如果注入的属性为null,你会从哪些方向排查”。既然写了这篇文章,就闲话不说,来看看Spring是如何解决循环依赖的,以及循环依赖的本质是什么。
循环依赖通常发生在默认单例Bean的属性注入中,例如几个Bean互相引用或甚至自己引用自己。原型Bean的场景则不支持循环依赖,通常会抛出BeanCurrentlyInCreationException。Spring默认单例的属性注入场景下如何支持循环依赖呢?
Spring内部维护了三个Map,常被称为三级缓存。在DefaultSingletonBeanRegistry类中可以看到这三个Map:singletonObjects、singletonFactories和earlySingletonObjects。后两个Map主要用于创建Bean的过程,完成后会被清空,最终Bean会存储在singletonObjects中。
这三个Map的作用类似于“三级缓存”,其中earlySingletonObjects用于存储Bean的早期引用,singletonFactories用于存储创建Bean的原始工厂方法,而singletonObjects则存储最终的单例Bean实例。Spring通过这三个Map协同工作,解决循环依赖问题。
如果让你从零实现一个支持循环依赖的依赖注入框架,你会怎么做?假设需要将指定类实例化为单例,并注入类字段为单例,同时支持循环依赖。举个例子,类A中有字段B,类B中有字段A。这其实与two sum问题很相似。
在two sum问题中,我们需要找到数组中两个数之和等于目标值的两个索引。这可以通过一次遍历和一个HashMap来实现:先将当前数存入HashMap,然后检查是否有与目标值相关的数已经存在于HashMap中。如果有,返回这两个数的索引;否则,将当前数存入HashMap。这种方法类似于Spring如何处理循环依赖:通过缓存(HashMap)记录已创建的Bean实例,当需要注入的Bean已经存在时,直接从缓存中取用;否则,创建新实例并注入。
这就是循环依赖的本质,而不是Spring如何解决循环依赖。理解这一点可以帮助我们更好地理解Spring的实现机制,而不是陷入阅读源码的泥潭。
如果你是上文提到的“陷入阅读源码泥潭”的读者,这篇文章或许能帮到你。如果你对Spring的复杂性有疑问,不妨跳出“牛角尖”,看看Java世界还有其他值得探索的领域。技术世界永远充满新鲜事物,保持好奇心是成长的关键。
转载地址:http://ddqfk.baihongyu.com/