springboot bean的循环依赖实现 源码分析
springboot bean的循环依赖实现 源码分析
本文基于springboot版本2.5.1
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
本文主要聚焦在循环依赖部分,主要用单例bean来进行讲解,其他bean实现的流程不会过多涉及。
1、什么叫循环依赖呢
简单来说就是springboot容器中的多个bean,如A、B两个bean,A有属性B需要注入,B有属性A需要注入,形成相互依赖的情况。
看下代码,就是类似下面这种情况
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
上面有两个bean,分别是ServiceA,ServiceB。ServiceA中需要注入ServiceB的实例,ServiceB中需要注入ServiceA的实例,这就是一种典型的循环依赖,其他还有方法参数循环依赖的场景等等,但是它们的内部实现基本是一样的。
2、具体出现循环依赖的代码逻辑
获取bean的方法
在springboot中默认的beanFactory是DefaultListableBeanFactory,在我们获取bean对象的时候,如果bean对象存在就直接返回,如果不存在,就先创建bean对象再返回。
我们先看下我们获取bean的常用方法都有哪些
public <T> T getBean(Class<T> requiredType) throws BeansException
public Object getBean(String name) throws BeansException
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException
public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
public void preInstantiateSingletons() throws BeansException
常用的获取bean的方法主要有上面几个和它们的重载版本,对于第3行、第4行、第5行最终都会调用到第2行的方法来获取bean。而它也会通过调用doGetBean(在AbstractBeanFactory这个类中)来获取bean
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
第1行的方法也会调用doGetBean来获取bean
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
throws BeansException { return doGetBean(name, requiredType, args, false);
}
所有最终获取bean的方法都是
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
这个方法,这个方法是protected的,是不对外提供的。所以我们不能直接调用它,只能通过上面提供的5个方法来获取bean对象。
下面我们从doGetBean这里来看下serviceA创建的过程
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
//如果bean之前存在,这里返回的shareInstance就是非空,就会从后面的if分支中返回,如果bean之前不存在,就会执行后面的bean创建及注入属性的过程
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
......
//如果当前不只是检查,而且是创建bean,这个参数就是false,在这里就会做个bean创建的标记,把beanName 加到alreadyCreated里面去
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
//我们当前要创建的bean是单例的,就会走到这里去,下面我们走到里面的调用去看看
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} }
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
......
//这里会把当前bean的名字加入到当前正在创建的单例对象集合singletonsCurrentlyInCreation中
beforeSingletonCreation(beanName);
......
try {
//这里就是调用上面的return createBean(beanName, mbd, args);这个方法,我们进这里面去看看
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
......
}
return singletonObject;
}
}@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
......
// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
//在这里获取要创建的bean的class对象
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
......
try {
//调用这里来创建,我们再走到这里面去看看
//3个参数分别为
//1、beanName bean对象的名字
//2、mbdToUseRootBeanDefinition对象,可以认为就是bean的元数据信息,包含bean的类对象,bean的类上注解,bean实际位置路径等等
//3、args bean对象的构造方法的实参,这里一般是空的
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
......
}protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException { ......
//真正创建bean对象是在这里,这里返回的instanceWrapper是bean对象的类实例的包装对象BeanWrapper
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//这里的bean就是实际创建的bean对象的类实例
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
......
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
//看上面的注释大概也能明白, 大概意思就是早期的单例缓存,为了解决由 BeanFactoryAware等等触发的循环依赖
//mbd.isSingleton() 表示bean是单例的(这个是bean对应的类上的,默认就是单例),
//this.allowCircularReferences 允许循环引用,这个是beanFactory的成员属性,默认也是true
//isSingletonCurrentlyInCreation(beanName) 表示是否在当前正在创建的bean集合中。beforeSingletonCreation(beanName);我们在前面执行过这句就加到正在创建的bean集合中了
//这里earlySingletonExposure 就是true了,会进到if分支中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//这句主要是将将() -> getEarlyBeanReference(beanName, mbd, bean) 这个lambda表达式存储到this.singletonFactories集合中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
} // Initialize the bean instance.
Object exposedObject = bean;
try {
//在这里就会进行属性填充,完成成员注入等等,也就是在这里serviceA这个bean会注入serviceB这个成员属性,我们走进这个方法去看看
populateBean(beanName, mbd, instanceWrapper);
......
}
...... return exposedObject;
}
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
......
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
//真正的属性注入是在这里完成的,aop也是在这里来完成的。这里是获取beanFactory中的InstantiationAwareBeanPostProcessor对bean对象进行增强
//如果属性注入用的是@Resource,就会用CommonAnnotationBeanPostProcessor来完成
//如果属性注入用的是@Autowired,就会用AutowiredAnnotationBeanPostProcessor来完成
//如果是AOP 就会使用InfrastructureAdvisorAutoProxyCreator来生成对应的代理对象
//我们这里使用的是@Autowired,所以会用AutowiredAnnotationBeanPostProcessor来完成注入。我们走到它的postProcessProperties的去看看
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
......
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
//这里主要是获取bean的类属性和方法上的org.springframework.beans.factory.annotation.Autowired,org.springframework.beans.factory.annotation.Value注解来进行注入
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
//继续进去看看
metadata.inject(bean, beanName, pvs);
}
......
}
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
......
//对每一个属性分别进行注入,继续进去
element.inject(target, beanName, pvs);
}
}
}
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
//如果之前缓存过就从缓存取,我们是第一次注入,所以之前没有缓存,不会走这个分支
if (this.cached) {
try {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
catch (NoSuchBeanDefinitionException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve
value = resolveFieldValue(field, bean, beanName);
}
}
else {
//会走这里来解析字段的值,再进去
value = resolveFieldValue(field, bean, beanName);
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
//创建字段的包装类DependencyDescriptor
DependencyDescriptor desc = new DependencyDescriptor(field, this.required); try {
//调用这里完成对应字段值的查找,再进去
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
//获取到值之后,进行缓存
if (!this.cached) {
......
}
this.cachedFieldValue = cachedFieldValue;
this.cached = true;
}
}
return value;
}
}
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
//当前的类是一个普通的class,会走到这里面,由于我们的bean没有Lazy注解,所以这里返回时null,走到下面的if分支
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
//在这里我们看下这里的入参。
//descriptor是包含了需要注入的字段的信息。
//requestingBeanName是当前正在创建的bean的名字serviceA,
//autowiredBeanNames是当前需要注入的字段的对应的bean的名字的集合,这里只有serviceB
//typeConverter这个是进行注入时做类型转换的,这里我们可以不用关注这个
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
......
if (instanceCandidate instanceof Class) {
//又会调用到这里,我们再进入到DependencyDescriptor的resolveCandidate去看看
//注意:这里的autowiredBeanName是我们需要注入的属性名这里是serviceB
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
......
}public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
//看到没,到这里就出现循环调用了,到这里又会重新调用beanFactory.getBean("serviceB")去创建serviceB的bean对象,完成后注入到serivceA对应的Bean上的属性上来,这时代码又会从本节开头的位置开始执行,先创建serviceB对象实例,再去注入serviceB对象的serviceA属性。
//最终会执行到beanFactory.getBean("serviceA")这里
return beanFactory.getBean(beanName);
}
就是下面图的样子
3、解决循环依赖的代码实现
接着上面的beanFactory.getBean("serviceA")这行代码我们继续往下看
这次又会走到这里
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
//我们第二部分就是从这里开始的,又走回来了,但这次又会有所不同
String beanName = transformedBeanName(name);
Object beanInstance;
// Eagerly check singleton cache for manually registered singletons.
//这次我们这里返回的就不是空了,sharedInstance对象的值就是对应serviceA的bean对象了,这次就会从if分支中返回,而之前我们不会进这里的if分支而是进入else分支导致后面出现了循环依赖的问题,这次我们进到这个方法看看
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
@Nullable
public Object getSingleton(String beanName) {
//再点进去
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
//这里由于当前的serviceA bean还没完成创建,所以这里singletonObject返回的是空,
//再看看 isSingletonCurrentlyInCreation(beanName)这里,由于我们在创建serviceA过程中有这么一句beforeSingletonCreation(beanName)(不清楚这句的搜索下本文,上面就有讲到),所有这个条件是true。这时我们就会进入if分支中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
//由于我们是第一次进入这里,所以this.earlySingletonObjects.get(beanName)返回的也是null
//我们的入参 allowEarlyReference是true,会继续进到这个if分支中
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
//这里的singletonObject还是null,继续进到if分支
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
//最终会走到这里,在创建serviceA对象之后,属性注入之前,执行了这句 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))(不清楚的搜索下本文,上面有说到),所以这里返回的singletonFactory是个lamdba表达式,getEarlyBeanReference(beanName, mbd, bean))附带了3个参数,第一个beanName是serivceA,mdb是对应serviceA的附带serviceA元数据信息的RootBeanDefinition对象,bean就是创建出来的serviceA对象
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//这里就会调用getEarlyBeanReference(beanName, mbd, bean)对serviceA对象进行一个getEarlyBeanReference增强后返回,返回后放置到earlySingletonObjects中,并从singletonFactories中删除
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects中,并从.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
最终在serviceA 这个bean创建完成后,就会从singletonsCurrentlyInCreation移除掉
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
......
finally {
//在这里从singletonsCurrentlyInCreation中移除掉
afterSingletonCreation(beanName);
}
if (newSingleton) {
//将serviceA bean对象添加到singletonObjects,registeredSingletons中
//从singletonFactories,earlySingletonObjects中移除掉
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
所以整个获取serviceA的流程就是这样了,
1、首先去创建serviceA这个bean,
由于它有个属性serviceB,在创建完serviceA对象后,就会去进行serviceB的属性注入,
这时由于serviceB之前没有生成,这时又会去创建serviceB这个bean,
先创建serviceB对象,然后再进行serviceA这个属性的注入,
继续去获取serviceA这个bean,第二次进入获取serviceA的流程,这时从之前缓存的lambda表达式中获取到之前创建的serviceA的引用返回。
2、总结下关键的代码点
- 创建bean对象之前调用beforeSingletonCreation(beanName)将bean对象名字添加到singletonsCurrentlyInCreation集合中
- 创建bean对象对应的类实例后调用addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));添加到singletonFactories中
- 在循环依赖中第二次调用到创建bean对象时,调用getSingleton(beanName, true)时,从singletonFactories中返回对应的早期bean对象的引用
springboot bean的循环依赖实现 源码分析的更多相关文章
- Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗
开心一刻 吃完晚饭,坐在院子里和父亲聊天 父亲:你有什么人生追求? 我:金钱和美女 父亲对着我的头就是一丁弓,说道:小小年纪,怎么这么庸俗,重说一次 我:事业与爱情 父亲赞赏的摸了我的头,说道:嗯嗯, ...
- SpringBoot启动代码和自动装配源码分析
随着互联网的快速发展,各种组件层出不穷,需要框架集成的组件越来越多.每一种组件与Spring容器整合需要实现相关代码.SpringMVC框架配置由于太过于繁琐和依赖XML文件:为了方便快速集成第三 ...
- SpringBoot中Tomcat和SpringMVC整合源码分析
概述 SpringBoot中集成官方的第三方组件是通过在POM文件中添加组件的starter的Maven依赖来完成的.添加相关的Maven依赖之后,会引入具体的jar包,在SpringBoot启动 ...
- spring依赖注入源码分析和mongodb自带连接本地mongodb服务逻辑分析
spring依赖注入本质是一个Map结构,key是beanId,value是bean对应的Object. autowired是怎么将定义的接口与对应的bean类建立联系? <bean name= ...
- Spring IOC 容器源码分析 - 循环依赖的解决办法
1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...
- Spring IOC 容器源码分析 - 填充属性到 bean 原始对象
1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...
- Spring IOC 容器源码分析 - 创建原始 bean 对象
1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...
- Spring IOC 容器源码分析 - 获取单例 bean
1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...
随机推荐
- C#常见的文件路径Api
我们经常有遇到要处理文件路径的需求,那么一般我们常见的有几种: 程序下面的文件 临时目录下的文件 获取程序下面的文件 首先我们创建了实例解决方案: 其中调用链是:Main.Shell->FooA ...
- utf8字符集下的比较规则
前言: 在MySQL中,比较常用的字符集是utf8和utf8mb4.这两个字符集是类似的,utf8是utf8mb3的别名,所以之后在MySQL中提到utf8就意味着使用1~3个字节来表示一个字符,如果 ...
- python爬虫——《瓜子网》的广州二手车市场信息
由于多线程爬取数据比单线程的效率要高,尤其对于爬取数据量大的情况,效果更好,所以这次采用多线程进行爬取.具体代码和流程如下: import math import re from concurrent ...
- [刷题] 3 Longest Substring Without Repeating Character
要求 在一个字符串中寻找没有重复字母的最长子串 举例 输入:abcabcbb 输出:abc 细节 字符集?字母?数字+字母?ASCII? 大小写是否敏感? 思路 滑动窗口 如果当前窗口没有重复字母,j ...
- Zabbix 监控介绍
Zabbix 监控介绍 1.Zabbix监控架构 2.Zabbix 优点 开源无软件成本投入 Server对设备性能要求低 支持设备多,自带多种监控模板 支持分布式集中管理,有自动发现功能,可以实现自 ...
- 攻防世界-WEB-新手练习区
附:|>>>攻防世界-WEB-高手进阶区<<<|
- 011.Kubernetes使用共享存储持久化数据
本次实验是以前面的实验为基础,使用的是模拟使用kubernetes集群部署一个企业版的wordpress为实例进行研究学习,主要的过程如下: 1.mysql deployment部署, wordpre ...
- C语言进阶日志二
1.一个功能模块最好使用一个文件夹包含 2.基本就需要几个文件,比如定时器取反 LED: bsp_led.c 和 bsp_led.h : 包含你需要的led外设GPIO的初始化代码 led_it.c ...
- docker0详解
docker0:https://blog.csdn.net/kubailing/article/details/87936501 veth pair详解:https://www.cnblogs.com ...
- Stream中的Peek操作
1.引言 如果你试图对流操作中的流水线进行调试, 了解stream流水线每个操作之前和操作之后的中间值, 该如何去做? 首先我们看一个例子, 使用forEach将流操作的结果打印出来. 1 /** 2 ...