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、具体出现循环依赖的代码逻辑

  1. 获取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对象。

  2. 下面我们从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的循环依赖实现 源码分析的更多相关文章

  1. Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗

    开心一刻 吃完晚饭,坐在院子里和父亲聊天 父亲:你有什么人生追求? 我:金钱和美女 父亲对着我的头就是一丁弓,说道:小小年纪,怎么这么庸俗,重说一次 我:事业与爱情 父亲赞赏的摸了我的头,说道:嗯嗯, ...

  2. SpringBoot启动代码和自动装配源码分析

    ​ 随着互联网的快速发展,各种组件层出不穷,需要框架集成的组件越来越多.每一种组件与Spring容器整合需要实现相关代码.SpringMVC框架配置由于太过于繁琐和依赖XML文件:为了方便快速集成第三 ...

  3. SpringBoot中Tomcat和SpringMVC整合源码分析

    概述 ​ SpringBoot中集成官方的第三方组件是通过在POM文件中添加组件的starter的Maven依赖来完成的.添加相关的Maven依赖之后,会引入具体的jar包,在SpringBoot启动 ...

  4. spring依赖注入源码分析和mongodb自带连接本地mongodb服务逻辑分析

    spring依赖注入本质是一个Map结构,key是beanId,value是bean对应的Object. autowired是怎么将定义的接口与对应的bean类建立联系? <bean name= ...

  5. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  6. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

  7. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

  8. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  9. Spring IOC 容器源码分析 - 获取单例 bean

    1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...

随机推荐

  1. Java安全之FastJson JdbcRowSetImpl 链分析

    Java安全之FastJson JdbcRowSetImpl 链分析 0x00 前言 续上文的Fastjson TemplatesImpl链分析,接着来学习JdbcRowSetImpl 利用链,Jdb ...

  2. 项目展示$\beta$

    项目 内容 课程:北航-2020-春-软件工程 博客园班级博客 要求 Beta阶段项目展示 我们在这个课程的目标是 提升团队管理及合作能力,开发一项满意的工程项目 这个作业在哪个具体方面帮助我们实现目 ...

  3. [笔记] 《c++ primer》显示器程序 Chapter7

    补充Sales_data没有体现出的其他类特性 Screen.h 1 #include <string> 2 #include <iostream> 3 4 class Scr ...

  4. mysql 配置文件概述

    mysql 配置文件概述 mysql 配置文件 mysql 的配置文件为 /etc/my.cnf 配置文件查找次序:若在多个配置文件中均有设定,则最后找到的最终生效 /etc/my.cnf --> ...

  5. 攻防世界(十二)upload1

    攻防世界系列 :upload1 1.打开题目,文件上传. 2.立即上传shell 1.php <?php @eval($_POST[root]); ?> 提示只能上传图片 3.burp改报 ...

  6. hugboy源库

    =[个人整理的一些源库,均来自网络]= -[Ubuntu]- #阿里源 Ubuntu 20.04 deb http://mirrors.aliyun.com/ubuntu/ focal main re ...

  7. shell应用之下载rpm包

    1 #!/bin/bash 2 read -p "选择下载老师的哪种源:(adv,base,cobbler,docker,mysql,mysql57,open,auto)" dow ...

  8. 【待写Java线程之线程终止 Interrupt 】

    参考:https://bbs.csdn.net/topics/280082639 interrupt()方法不会中断一个正在运行的线程.这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线 ...

  9. Spring Cloud 升级之路 - 2020.0.x - 7. 使用 Spring Cloud LoadBalancer (2)

    本项目代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford 我们使用 Spri ...

  10. IDEA2021.1 安装教程

    工欲善其事必先利其器. 一.下载 IDEA 官方下载地址: https://www.jetbrains.com/zh-cn/idea/download/ 二.安装 IDEA 注:安装IDEA之前需要我 ...