SpringBoot Bean实例化流程解析

 更新时间:2023年08月28日 09:58:24   作者:LBruse  
在SpringBoot启动过程中会执行refreshContext()方法,而在其执行过程中,又会调用finishBeanFactoryInitialization()方法,该方法负责了Bean的实例化,那么本文将从源码跟读的角度来解析一下具体流程

前置工作

新建一个RestService,代码如下

package geek.springboot.application.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
/**
 * Rest Service
 *
 * @author Bruse
 */
@Slf4j
@Service
public class RestService {
    @PostConstruct
    public void init() {
        log.info("RestService init....");
    }
}

finishBeanFactoryInitialization

来到 AbstractApplicationContext refresh() 方法,在该方法中调用了 finishBeanFactoryInitialization() 方法

image.png

判断是否存在conversionService

首先判断当前IOC容器中是否存在 conversionService ,有的话将其设置到 BeanFactory

image.png

判断是否已有BeanFactoryPostProcessor

接下来判断当前 BeanFactory 中是否存在 BeanFactoryPostProcessor ,没有的话注册一个默认的实现

image.png

初始化LoadTimeWeaverAware

接着查询当前IOC容器中是否存在 LoadTimeWeaverAware 的实现,有的话则进行初始化。该类是使用AOP时做织入的一个工具类,一般情况下开发不会使用到它,所以这里其实并不会执行到循环体中的 getBean()

image.png

停止使用TempClassLoader

接下来将 BeanFactory tempClassLoader 属性设置为null

image.png

调用freezeConfiguration()

接下来调用 freezeConfiguration() 方法,该方法主要是做一个 冻结 声明,即声明在此期间无法再向 BeanFactory 注册新的Bean定义

image.png

image.png

调用preInstantiateSingletons()

最后也是最关键的,调用 preInstantiateSingletons() 方法进行Bean的实例化

image.png

实例化Bean

最终其实会调用到 DefaultListableBeanFactor preInstantiateSingletons() 方法。

首先会获取当前所有Bean定义的名称,并进行遍历

image.png

Tips:除了之前定义的service,controller,config...也包含了Spring内置的一些Bean

接着根据名称获取相关的 BeanDefinition ,也就是Bean的定义,它包括了Bean的一些元信息,比如是否单例,是否延迟初始化,该Bean依赖项等。

image.png

Tips:这里为了避免混淆视听,我事先已经在debug断点处加上了触发条件,只有beanName是restService时,断点才生效。

image.png

可以看到Spring会先根据 BeanDefinition 判断出当前类是否 抽象类 ,是否 单例 ,是否 延迟加载 。只有当当前 BeanDefinition 满足既不是 抽象类 ,也不 延迟加载 单例 时,才会进行操作。

接着还会判断当前 Bean 是否实现了 FactoryBean 的接口,是的话则还要对 Bean 做一些处理。

因为之前创建的 RestService 没有实现 FactoryBean 接口,所以直接调用 getBean 方法

image.png

doGetBean

接着走到 AbstractBeanFactory doGetBean() 方法,该方法主要作用就是返回一个指定的Bean实例

image.png

transformedBeanName

首先是对 Bean 的名称做一个处理,其中包含了若 BeanName 中包含了 FACTORY_BEAN_PREFIX 的话,则将其从 BeanName 中删除的逻辑

image.png

同时如果有设置 别名 的话,也会获取真正的 BeanName 再返回

image.png

getSingleton

接着进入 DefaultSingletonBeanRegistry getSingleton 方法,该方法主要作用是 返回给定名称注册的单例对象

image.png

虽然这里逻辑很长,还用到了 双检锁 机制,但是其实从 singletonObjects 中检查是否有对应 beanName Bean 存在时,因为是第一次创建,所以 singletonObjects 中并不存在相关的 Bean ,直接没进 if 方法体,就返回 null

image.png

isPrototypeCurrentlyInCreation

image.png

接着执行 AbstractBeanFactory isPrototypeCurrentlyInCreation 方法,该方法主要是检查该 beanName 相关的 Bean 是否在当前线程创建中,因为我们这里还没有进行创建,所以这里方法返回false。

获取BeanFactory

image.png

接着尝试获取父类 BeanFactory ,并调用父类 BeanFactory getBean 方法获取 Bean ,但是因为当前的 beanFactory 已经是最顶级的了,所以直接跳过大段代码。

Tips:Spring中BeanFactory跟JVM实现双亲委派机制的ClassLoader一样,也存在子级父级关系

markBeanAsCreated

接着执行 markBeanAsCreated 方法,这里也用到了 双检锁 机制,方法很简单,就是将 beanName 添加到 alreadyCreated 当中,算是做一个标识,标识当前 beanName 对应的 Bean 正在创建当中。

image.png

getMergedLocalBeanDefinition

接着调用 getMergedLocalBeanDefinition 方法获取 BeanDefinition ,并检查是否是抽象类,是的话直接抛出异常

image.png

image.png

检查是否存在依赖

image.png

接着检查当前要实例化的 Bean 是否和别的Bean存在依赖关系,是的话得先把所依赖的 Bean 创建好,才能继续实例化当前的 Bean

因为 RestService 中没有依赖什么别的 Bean ,所以这里略过一段代码

根据作用域创建Bean

接着便是根据作用域的不同,使用不同方式创建 Bean

image.png

getSingleton

这里进入 DefaultSingletonBeanRegistry getSingleton 方法

image.png

这里首先还是会检查当前 Bean 是否已初始化,是的话直接返回

image.png

createBean

接着调用 ObjectFactory getObject 方法,因为传参时是传递了一个 匿名内部类 ,所以重新回到 AbstractBeanFactory ,可以看到调用了 createBean 方法

image.png

resolveBeanClass

接着来到 AbstractAutowireCapableBeanFactory createBean 方法,首先会调用 AbstractBeanFactory resolveBeanClass 方法对 BeanDefinition 对应的 Class 做一个解析,这里因为之前已经过了,所以直接返回。

image.png

image.png

resolveBeforeInstantiation

image.png

接着来到 resolveBeforeInstantiation 方法,可以看到如果它返回的 Bean 不为空,那么将直接返回,意味着 Bean 实例化完成。

接着深入查看具体实现细节,可以看到会判断当前IOC容器是否存在 InstantiationAwareBeanPostProcessor 接口的实现

image.png

如果存在 InstantiationAwareBeanPostProcessor 接口实现,则会调用其 postProcessBeforeInstantiation 方法,如果该方法返回值不为空,那么直接返回,并且调用 postProcessAfterInitialization 方法再对返回值做一些处理

image.png

image.png

因为当前项目中并没有存在 InstantiationAwareBeanPostProcessor 接口实现,所以直接返回的是 null

doCreateBean

所以初始化 Bean 的重任还是交给了 doCreateBean 方法

image.png

首先判断当前 Bean 是否单例,是的话将其从 factoryBeanInstanceCache 中移除

image.png

createBeanInstance

接着进入 createBeanInstance 方法,首先调用 getInstanceSupplier 方法判断当前 BeanDefinition 是否从其它配置加载的,然后调用 getFactoryMethodName 方法判断当前 BeanDefinition 是否存在工厂方法, RestService 两个条件都不满足,所以会一路执行到后续代码。

image.png

determineConstructorsFromBeanPostProcessors

接着执行到 determineConstructorsFromBeanPostProcessors 方法,该方法主要是 确定使用哪个构造器来初始化Bean

image.png

可以看到内部实现其实是依靠调用 SmartInstantiationAwareBeanPostProcessor determineCandidateConstructors 方法来进行确定的

image.png

但其实最后返回的是 null

image.png

instantiateBean 

最后来到 instantiateBean 方法,由注释也可以看出,该方法就是在 Bean 无需做特殊处理,调用默认无参构造函数即可初始化时调用的。

image.png

image.png

首先调用 getInstantiationStrategy 方法获取 实例化策略 ,可以看到默认 实例化策略 CglibSubclassingInstantiationStrategy

image.png

instantiate

接着调用 instantiate 方法,在该方法中利用反射机制获取 类默认构造函数

image.png

BeanUtils.instantiateClass

最后调用 BeanUtils instantiateClass 方法进行构建,可以看出其实该方法内部就是用了Java的 反射机制 进行类的实例构建

image.png

BeanWrapper

Bean 实例成功创建后,会创建 BeanWrapper 实例来对 Bean 实例做一个包装,并调用 initBeanWrapper 方法对 BeanWrapper 进行初始化操作

image.png

可以看到最后返回的不是 BeanInstance ,而是把 BeanInstance 给包裹了一层,返回的 BeanWrapper

image.png

applyMergedBeanDefinitionPostProcessors

接着调用 applyMergedBeanDefinitionPostProcessors 方法,该方法本质即获取所有 MergedBeanDefinitionPostProcessor 实现,并逐个调用其 postProcessMergedBeanDefinition 方法

image.png

image.png

populateBean

接着执行 populateBean 方法,该方法主要用来填充当前的 BeanInstance

image.png

会获取当前 BeanDefinition 的所有 Property ,并判断以何种方式进行自动注入,根据类型?根据名称?

image.png

还可以看到会尝试获取所有 InstantiationAwareBeanPostProcessor 实现,并调用其 postProcessProperties 方法

image.png

总结

在Spring实例化Bean的过程中, BeanDefinition 几乎贯穿了整个流程,而 BeanDefinition 是一个对象在Spring中的描述,Spring通过操作 BeanDefinition 来完成 Bean 的实例化和属性注入,而实例化的过程中又使用到了Java中非常基础且重要的—— 反射

以上就是SpringBoot Bean实例化流程解析的详细内容,更多关于SpringBoot Bean实例化的资料请关注脚本之家其它相关文章!

相关文章

  • Java中Minio的基本使用详解

    Java中Minio的基本使用详解

    这篇文章主要介绍了Java中Minio的基本使用详解,MinIO 是一个基于Apache License v2.0开源协议的对象存储服务,它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,需要的朋友可以参考下
    2024-01-01
  • Java本地部署DeepSeek的详细步骤

    Java本地部署DeepSeek的详细步骤

    本文主要介绍了使用Java在本地部署DeepSeek的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-10-10
  • Java多线程:生产者与消费者案例

    Java多线程:生产者与消费者案例

    这篇文章主要介绍了Java并发编程中的生产者与消费者模型简述,多线程并发是Java编程中最终要的部分之一,需要的朋友可以参考下,希望能给你带来帮助
    2021-07-07
  • SpringMVC 拦截器的使用示例

    SpringMVC 拦截器的使用示例

    这篇文章主要介绍了SpringMVC 拦截器的使用示例,帮助大家更好的理解和学习使用SpringMVC,感兴趣的朋友可以了解下
    2021-04-04
  • 详解javaweb中jstl如何循环List中的Map数据

    详解javaweb中jstl如何循环List中的Map数据

    这篇文章主要介绍了详解javaweb中jstl如何循环List中的Map数据的相关资料,希望通过本文能帮助到大家,让大家理解掌握这部分内容,需要的朋友可以参考下
    2017-10-10
  • 使用Java 压缩文件打包tar.gz 包的详细教程

    使用Java 压缩文件打包tar.gz 包的详细教程

    本文带领大家学习如何使用Java 压缩文件打包tar.gz 包,主要通过 Apache compress 工具打包,通过示例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2021-05-05
  • SpringBoot项目中接口防刷的完整代码

    SpringBoot项目中接口防刷的完整代码

    本文通过实例代码给大家介绍了SpringBoot项目中接口防刷的方法,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-07-07
  • IDEA 2022最新激活码注册码超详细教程(亲测激活有效)

    IDEA 2022最新激活码注册码超详细教程(亲测激活有效)

    这篇文章主要介绍了IDEA 2022最新激活码超详细教程(亲测激活至2099年),本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • 解决IDEA报错Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded

    解决IDEA报错Caused by: org.springframework.boot.web.se

    遇到IDEA启动报错,可尝试以下方法:打开项目设置(Ctrl+Shift+Alt+S),将JDK版本修改为1.8;或者检查TomCat依赖,若有问题可尝试删除,此外,确保每次拉取项目后,maven地址设置为本地,并且JDK版本设置为1.8,以上为个人解决经验,希望对大家有所帮助
    2024-09-09
  • Java Collection集合iterator方法解析

    Java Collection集合iterator方法解析

    这篇文章主要介绍了Java Collection集合iterator方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01

最新评论