0
点赞
收藏
分享

微信扫一扫

从源码角度分析spring是如何解决循环依赖的


本文基于spring 5.2.x 调试,参考文档: https://docs.spring.io/spring/docs/5.2.4.RELEASE/spring-framework-reference/core.html#beans-constructor-injection 。

下文中提到的bean的创建是指的经过了一部分bean生命周期并存入单例池的这个过程,且bean和对象是有区别的,个人认为没有存入单例池的都不是真正意义上的bean,只能叫对象。聊起spring循环依赖,首先我们需要了解什么叫循环依赖,比如我们定义了两个Bean:A和B,A 注入 B, B 注入A,这就是循环依赖。然后我们要知道spring默认是支持循环依赖的,由spring内部定义了一个变量allowCircularReferences且默认为true可以看出,spring也提供了一个api来关闭,这里先不讨论。但是spring也有解决不了循环依赖的情况,最终会抛出异常,下面先演示解决不了的场景:


  1. 构造注入:
    从源码角度分析spring是如何解决循环依赖的_属性注入
    其实这一点在​​官方文档​​中有说明:
    从源码角度分析spring是如何解决循环依赖的_spring_02
  2. 原型模式:
    从源码角度分析spring是如何解决循环依赖的_属性注入_03
    我们再看一个解决循环依赖成功的例子:
    从源码角度分析spring是如何解决循环依赖的_初始化_04
    抛开循环依赖不讲,以A为例,我想先大概讲一下单例Bean的创建过程:
    这幅图中总共做了五件事,具体请看图中描述。
    从源码角度分析spring是如何解决循环依赖的_属性注入_05
    然后再提供这个过程中的调用链图:
    从源码角度分析spring是如何解决循环依赖的_spring_06
    实际上到执行完AbstractApplicationContext.java的invokeBeanFactoryPostProcessors()时,beanDefinitionMap中就已经有8个元素了,继续往下看bean的创建过程:
    从源码角度分析spring是如何解决循环依赖的_初始化_07
    将单例bean存入singletonObjects时,实际上就已经完成一个bean的创建了(再次说明bean的创建过程实际上没有那么简单)。从上图我们可以看出当控制台打印出“----------A----------” 之后,不久又调用了populateBean方法(填充bean,也就是属性注入),这时候控制台又打印出“----------B----------”,这说明创建A的时候会去创建B,因为注入了B,所以我们可以大胆的猜测循环依赖就是发生在populateBean中。

现在我先梳理一下大概的流程:当初始化Bean A并执行到populateBean方法时,发现A依赖了Bean B,这个时候就会去创建B,然后B在属性注入过程中发现又依赖了A,但这个时候发现A对象(这里还不是bean,没有完成属性注入,没有存入单例池singletonObjects)正在创建,存在于缓存中,所以直接取出来,当B完成属性注入且存入单例池,然后才会继续完成A的属性注入。上图其实录制的不是很好,因为在执行addSingleton方法时应该先看一下singletonObjects中的元素,这个时候你会发现已经存在Bean B了,也就是B先于A成为一个单例bean。

接下来再根据源码分析一下这个流程(只分析最重要的几个流程),先从AbstractBeanFactory#doGetBean()看起:

接着上一幅图:

这个时候已经完成了对象B的创建,且也执行到了populateBean方法,准备注入a,接着往下看:

判断a或b是否正在创建是org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()中调用了 isSingletonCurrentlyInCreation(beanName),实际上是判断Set(singletonsCurrentlyInCreation)中是否包含这个beanName,而这个singletonsCurrentlyInCreation是在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()中调用beforeSingletonCreation(beanName)执行add操作将beanName存入的,这两个getSingleton方法属于重载,并不是同一个方法。

最终执行完所有流程后控制台返回:

从源码角度分析spring是如何解决循环依赖的_属性注入_11

总结:spring的循环依赖主要发生在属性注入时【AbstractAutowireCapableBeanFactory#populateBean();】为了解决这个问题,spring引入了三级缓存: singletonObjects、earlySingletonObjects、singletonFactories。同时,如果是构造注入或者原型Bean导致的循环依赖是解决不了的,这里还有一点要注意,如果是原型Bean,且没有手动去调用【如ac.getBean(A.class)】是不会报错的,因为原型bean这时候并没有真正的初始化。如下图:



举报

相关推荐

0 条评论