Spring 提供了关闭循环依赖的方法。
boolean allowCircularReferences
我们从 doGetBean
开始分析。
Spring 在初始化和 getBean()
时,都会调用这个方法。
删减版doGetBean
//...
表示被我省略掉的一些代码
代码很长,我们节选一些片段来解析
先检查一下单例池当中有没有手动注册的单例对象:
单例池:
Spring 中所有实例化好的单例 Bean 都存放在这个map当中。
判断当前的类是否在正在创建的原型集合当中,如果是,则抛异常:
说明一般情况下,原型不支持循环依赖。
prototypesCurrentlyInCreation:
判断当前 Bean 是否是单例,如果是,则把 Bean 创建出来。
判断当前实例化的 Bean 是否在正在被销毁:
singletonsCurrentlyInDestruction:
beforeSingletonCreation(beanName):
判断当前 Bean 是否正在被创建。因为 Spring 不管创建 Prototype Bean 还是 Singleton Bean,当他需要正式创建 Bean 的时候他会将这个 Bean add 到一个Set 中去
this.inCreationCheckExclusions.contains(beanName)
判断当前需要创建的 Bean 是否在 Exclusions 集合中,程序员可以提供一些 Bean 不被 Spring 初始化(哪怕被扫描到了,也不初始化),那么这些被提供的 Bean 便会存在这个集合当中;this.singletonsCurrentlyInCreation.add(beanName)
把当前正在创建的 Bean 添加到正在创建的集合中。singletonsCurrentlyInCreation:
继续往下执行到singletonObject = singletonFactory.getObject();
这里的 singletonFactory 就是上面传进来的那个 Lamda 表达式,我再贴一下:
所以我们要进到 createBean
方法中去。
从调用栈中我们也可以看出来这点:
这里主要是 doCreateBean
方法:
调用 doCreateBean
后,x 和 y 都完成实例化了(循环依赖也完成了):
下面着重分析一下 doCreateBean
这个方法,上面我们分析过一个名字类似的叫 doGetBean
,别搞混了:
createBeanInstance(beanName, mbd, args)
:
运行完以后,x 的构造方法就被调用了:
但是此时 x 对象只是被创建了,它还不是 Spring Bean,它的生命周期才刚刚开始。
往下执行到 earlySingletonExposure
变量赋值处:
mbd.isSingleton()
我们的 Bean 都是单例的,所以此处为 truethis.allowCircularReferences
默认允许循环依赖,此处为 trueisSingletonCurrentlyInCreation(beanName)
x 在正在创建的 Bean 的集合中,此处为 true这里又印证了上面我们说的关闭循环依赖的方法。
继续向下执行,这里又是一个 Lamda 表达式:
addSingletonFactory(beanName,singletonFactory)
添加一个单例工厂,这个工厂可以产生我们需要的半成品的 Bean 。
addSingletonFactory
:
下面这几行代码都很重要,依次分析下这几行代码:
先判断单例池 singletonObjects
中是否已经存在这个 Bean ,如果存在说明循环依赖已经完成。这里,我们称 singletonObjects
为一级缓存 。
如果一级缓存中不存在,将工厂对象 put 到 singletonFactories
中去,我们称之为二级缓存。
从 earlySingletonObjects
中移除这个 Bean ,我们称 earlySingletonObjects
为三级缓存。
先记住这三个缓存,我们先往下看,后面再来分析他们。
populateBean
这个方法完成了属性注入:
我们观察一下这行代码执行前后:
执行前:
只实例化了 x,y 还没实例化
执行后:
x 注入 y ,y 也注入了 x ,循环依赖完成!
然后绕来绕去又回到了 getSingleton
:
但是这个 getSingleton
方法是我们上面看到的那个的兄弟(重载):
下面分析下这个兄弟:
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName))
成立进入分支singletonObject = this.earlySingletonObjects.get(beanName);
从三级缓存中获取 x,根据前面的分析三级缓存当中现在什么都没有,故而返回nllif (singletonFactory != null)
成立,进入分支ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
从二级缓存中获取一个 ObjectFactory 工厂对象,二级缓存中存在创建 x 的工厂,故而可以获取到singletonObject = singletonFactory.getObject();
拿到一个半成品的 x Beanthis.earlySingletonObjects.put(beanName, singletonObject);
把 x 放到三级缓存,this.singletonFactories.remove(beanName);
把二级缓存中 x 清除