图解Spring源码之循环依赖

文章目录

    • 准备工作
    • 关闭循环依赖
    • 开始调试
    • AbstractBeanFactory#doGetBean
    • getSingleton(String beanName, ObjectFactory singletonFactory)
    • AbstractAutowireCapableBeanFactory#createBean(beanName, mbd, args)
    • doCreateBean
    • 总结
    • 参考资料

Spring默认在 单例非构造方法注入的情况下是支持循环依赖的。

准备工作

图解Spring源码之循环依赖_第1张图片
图解Spring源码之循环依赖_第2张图片
图解Spring源码之循环依赖_第3张图片
图解Spring源码之循环依赖_第4张图片

图解Spring源码之循环依赖_第5张图片

关闭循环依赖

Spring 提供了关闭循环依赖的方法。

boolean allowCircularReferences

图解Spring源码之循环依赖_第6张图片

开始调试


我们从 doGetBean 开始分析。

AbstractBeanFactory#doGetBean

Spring 在初始化和 getBean() 时,都会调用这个方法。

删减版doGetBean
//... 表示被我省略掉的一些代码
图解Spring源码之循环依赖_第7张图片

代码很长,我们节选一些片段来解析

先检查一下单例池当中有没有手动注册的单例对象:
图解Spring源码之循环依赖_第8张图片
单例池:
Spring 中所有实例化好的单例 Bean 都存放在这个map当中。
图解Spring源码之循环依赖_第9张图片
判断当前的类是否在正在创建的原型集合当中,如果是,则抛异常:
图解Spring源码之循环依赖_第10张图片
说明一般情况下,原型不支持循环依赖。

prototypesCurrentlyInCreation:
图解Spring源码之循环依赖_第11张图片
判断当前 Bean 是否是单例,如果是,则把 Bean 创建出来。
图解Spring源码之循环依赖_第12张图片

getSingleton(String beanName, ObjectFactory singletonFactory)

图解Spring源码之循环依赖_第13张图片
分析与循环依赖有关的代码片段:

判断当前实例化的 Bean 是否在正在被销毁:
图解Spring源码之循环依赖_第14张图片
singletonsCurrentlyInDestruction:
图解Spring源码之循环依赖_第15张图片
beforeSingletonCreation(beanName):

判断当前 Bean 是否正在被创建。因为 Spring 不管创建 Prototype Bean 还是 Singleton Bean,当他需要正式创建 Bean 的时候他会将这个 Bean add 到一个Set 中去
图解Spring源码之循环依赖_第16张图片

  • this.inCreationCheckExclusions.contains(beanName)判断当前需要创建的 Bean 是否在 Exclusions 集合中,程序员可以提供一些 Bean 不被 Spring 初始化(哪怕被扫描到了,也不初始化),那么这些被提供的 Bean 便会存在这个集合当中;
  • this.singletonsCurrentlyInCreation.add(beanName)把当前正在创建的 Bean 添加到正在创建的集合中。

singletonsCurrentlyInCreation:
图解Spring源码之循环依赖_第17张图片
继续往下执行到singletonObject = singletonFactory.getObject();
图解Spring源码之循环依赖_第18张图片
这里的 singletonFactory 就是上面传进来的那个 Lamda 表达式,我再贴一下:
图解Spring源码之循环依赖_第19张图片
所以我们要进到 createBean 方法中去。

从调用栈中我们也可以看出来这点:

AbstractAutowireCapableBeanFactory#createBean(beanName, mbd, args)


这里主要是 doCreateBean 方法:图解Spring源码之循环依赖_第20张图片
调用 doCreateBean 后,x 和 y 都完成实例化了(循环依赖也完成了):

doCreateBean

下面着重分析一下 doCreateBean 这个方法,上面我们分析过一个名字类似的叫 doGetBean ,别搞混了:
图解Spring源码之循环依赖_第21张图片
createBeanInstance(beanName, mbd, args):
图解Spring源码之循环依赖_第22张图片
运行完以后,x 的构造方法就被调用了:
但是此时 x 对象只是被创建了,它还不是 Spring Bean,它的生命周期才刚刚开始。

往下执行到 earlySingletonExposure 变量赋值处:
图解Spring源码之循环依赖_第23张图片

  • mbd.isSingleton() 我们的 Bean 都是单例的,所以此处为 true
  • this.allowCircularReferences 默认允许循环依赖,此处为 true
  • isSingletonCurrentlyInCreation(beanName) x 在正在创建的 Bean 的集合中,此处为 true

这里又印证了上面我们说的关闭循环依赖的方法。

继续向下执行,这里又是一个 Lamda 表达式:
图解Spring源码之循环依赖_第24张图片
addSingletonFactory(beanName,singletonFactory) 添加一个单例工厂,这个工厂可以产生我们需要的半成品的 Bean 。

addSingletonFactory
图解Spring源码之循环依赖_第25张图片
下面这几行代码都很重要,依次分析下这几行代码:

先判断单例池 singletonObjects 中是否已经存在这个 Bean ,如果存在说明循环依赖已经完成。这里,我们称 singletonObjects一级缓存
图解Spring源码之循环依赖_第26张图片
如果一级缓存中不存在,将工厂对象 put 到 singletonFactories 中去,我们称之为二级缓存
图解Spring源码之循环依赖_第27张图片
earlySingletonObjects 中移除这个 Bean ,我们称 earlySingletonObjects三级缓存
图解Spring源码之循环依赖_第28张图片
先记住这三个缓存,我们先往下看,后面再来分析他们。

populateBean 这个方法完成了属性注入:
图解Spring源码之循环依赖_第29张图片
我们观察一下这行代码执行前后:
执行前:
只实例化了 x,y 还没实例化

执行后:
x 注入 y ,y 也注入了 x ,循环依赖完成!

然后绕来绕去又回到了 getSingleton:
图解Spring源码之循环依赖_第30张图片
但是这个 getSingleton 方法是我们上面看到的那个的兄弟(重载):

下面分析下这个兄弟:
图解Spring源码之循环依赖_第31张图片

  • 按照惯例还是先从单例池中获取(一级缓存)x,前面说过获取不到
  • 然后判断 x 是否在正在创建Bean 的集合当中,前面分析过这个集合现在存在 x y
  • if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) 成立进入分支
  • singletonObject = this.earlySingletonObjects.get(beanName); 从三级缓存中获取 x,根据前面的分析三级缓存当中现在什么都没有,故而返回nll
  • if (singletonFactory != null) 成立,进入分支
  • ObjectFactory singletonFactory = this.singletonFactories.get(beanName); 从二级缓存中获取一个 ObjectFactory 工厂对象,二级缓存中存在创建 x 的工厂,故而可以获取到
  • singletonObject = singletonFactory.getObject(); 拿到一个半成品的 x Bean
  • this.earlySingletonObjects.put(beanName, singletonObject); 把 x 放到三级缓存,
  • this.singletonFactories.remove(beanName); 把二级缓存中 x 清除
  • 二级缓存中只存在一个 y 了,而三级缓存中多了一个 x

    至此,循环依赖完成。

总结

最后使用一张图来总结一下
图解Spring源码之循环依赖_第32张图片

参考资料

  • spring源码系列(一)——spring循环引用

你可能感兴趣的:(Spring)