• 【springboot系列】springboot启动过程源码分析,【最全源码源码,手把手教】


    基于2.6.1版本

    分析的话,一般从启动类,标注着 @SpringBootApplication 注解并且有
    着 main() 方法的类:

     源码如何切分?
    SpringApplication 中的静态 run() 方法并不是一步完成的,最终执行的源码如下:

     如何创建SpringApplication?

    创建即是 new 对象了,debug 跟进代码,最终执行的 SpringApplication 构造方法如下图:

     

     如上图中标注的注释,创建过程重用的其实分为 2、3、4 这三个阶段,下面将会一一介绍每个阶段做了什么事。

    设置应用类型

    这个过程非常重要,直接决定了项目的类型,应用类型分为三种,都在 WebApplicationType 这个枚举类中,如下:
    1.  NONE :顾名思义,什么都没有,正常流程走,不额外的启动 web容器 ,比如 Tomcat 。
    2.  SERVLET :基于 servlet 的web程序,需要启动内嵌的 servlet web容器,比如 Tomcat 。
    3.  REACTIVE :基于 reactive 的web程序,需要启动内嵌 reactive web容器。


    判断的依据很简单,就是加载对应的类,比如加载了 DispatcherServlet 等则会判断是 Servlet 的web程序。源码如下: 

     

     这里我引入了 spring-boot-starter-web ,肯定是 Servlet 的web程序。

    设置初始化器(Initializer)

     初始化器 ApplicationContextInitializer ,用于 IOC 容器刷新之前初始化一些组件,比如
    ServletContextApplicationContextInitializer 。
    那么如何获取初始化器呢?跟着上图中的代码进入,在 SpringApplication 中的如下图中的方法:

     相对重要的就是第一步获取初始化器的名称了,这个肯定是 全类名 了,详细源码肯定在
    loadFactoryNames() 方法中了,跟着源码进入,最终调用的是
    #SpringFactoriesLoader.loadSpringFactories() 方法。
    loadSpringFactories() 方法就不再详细解释了,其实就是从类路径 META-INF/spring.factories 中加载ApplicationContextInitializer 的值。
    在 spring-boot-autoconfigure 的 spring.factories 文件中的值如下图:

     上图中的只是一部分初始化器,因为 spring.factories 文件不止一个。

    下图中是项目中注入的初始化器,现实项目中并不止这些。


    这也告诉我们自定义一个 ApplicationContextInitializer 只需要实现接口,在 spring.factories 文
    件中设置即可。 

     设置监听器(Listener)

    监听器( ApplicationListener )这个概念在 Spring 中就已经存在,主要用于监听特定的事件
    ( ApplicationEvent ),比如IOC容器刷新、容器关闭等。
    Spring Boot 扩展了 ApplicationEvent 构建了 SpringApplicationEvent 这个抽象类,主要用于 SpringBoot 启动过程中触发的事件,比如程序启动中、程序启动完成等。如下图: 

    监听器如何获取?从源码中知道其实和初始化器( ApplicationContextInitializer )执行的是同一个方
    法,同样是从 META-INF/spring.factories 文件中获取。
    在 spring-boot-autoconfigure 的 spring.factories 文件中的值如下图: 

     spring.factories 文件不止一个,同样监听器也不止以上这些。

    项目中注入的一些监听器如下图:

     总结:

    SpringApplication 的构建都是为了 run() 方法启动做铺垫,构造方法中总共就有几行代码,最重要的部
    分就是设置应用类型、设置初始化器、设置监听器。
    注意:初始化器和这里的监听器都要放置在 spring.factories 文件中才能在这一步骤加载,否则
    不会生效,因此此时 IOC容器 还未创建,即使将其注入到 IOC容器 中也是不会生效的。

    下图是执行流程图:

     

     执行run()方法

    上面分析了 SpringApplication 的构建过程,现在到了启动的过程了。
    根据源码启动过程分为了8步:

    1. 获取、启动运行过程监听器

     SpringApplicationRunListener 这个监听器和 ApplicationListener 不同,它是用来监听应用程序启动过程的,接口的各个方法含义如下:

     如何获取运行监听器?

     在 SpringApplication#run() 方法中,源码如下:

     跟进 getRunListeners() 方法,其实还是调用了 loadFactoryNames() 方法从 spring.factories 文件中获取值,如下:

    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener 

    最终注入的是 EventPublishingRunListener 这个实现类,创建实例过程肯定是通过反射了,因此我们看看它的构造方法,如下图:

     这个运行监听器内部有一个事件广播器( SimpleApplicationEventMulticaster ),主要用来广播特定的事件
    ( SpringApplicationEvent )来触发特定的监听器 ApplicationListener 。
    EventPublishingRunListener 中的每个方法用来触发 SpringApplicationEvent 中的不同子类。

    如何启动运行监听器? 

     在 SpringApplication#run() 方法中,源码如下:

    1. //执行starting()方法
    2. listeners.starting(bootstrapContext, this.mainApplicationClass);

    执行 SpringApplicationRunListeners 的 starting() 方法,跟进去其实很简单,遍历执行上面获取的运行监听器,这里只有一个 EventPublishingRunListener 。因此执行的是它的 starting() 方法,源码如下图:

    上述源码中,其实只是执行了 multicastEvent() 方法,广播了 ApplicationStartingEvent 事件。至于 multicastEvent() 内部方法,其实就是遍历 ApplicationListener 的实现类,找到监听 ApplicationStartingEvent 这个事件的监听器,执行 onApplicationEvent() 方法。

    总结

    这一步其实就是广播了 ApplicationStartingEvent 事件来触发监听这个事件的 ApplicationListener 。

    1. 因此如果自定义了 ApplicationListener 并且监听了 ApplicationStartingEvent (应用程序开始启
    2. 动)事件,则这个监听器将会被触发。

    2. 环境构建

    这一步主要用于加载系统配置以及用户的自定义配置( application.properties ),源码如下,在 run() 方法中: 

    1. ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext,
    2. applicationArguments);

    prepareEnvironment 方法内部广播了 ApplicationEnvironmentPreparedEvent 事件,源码如下图:

     

    1. 环境构建这一步加载了系统环境配置、用户自定义配置并且广播了
    2. ApplicationEnvironmentPreparedEvent 事件,触发监听器。

     3. 创建IOC容器

    源码在 run() 方法中,如下: 

    context = this.createApplicationContext();

    跟进代码,真正执行的是 ApplicationContextFactory 方法,如下图:

     根据 webApplicationType 决定创建的类型,很显然,这里是 servlet ,因此创建的是
    AnnotationConfigServletWebServerApplicationContext 。

    这一步仅仅是创建了 IOC容器 ,未有其他操作。

    4. IOC容器的前置处理

     这一步是精华,在刷新容器之前做准备,其中有一个非常关键的操作:将启动类注入容器,为后续的自动化配置奠定基础。源码如下:

     prepareContext() 源码解析如下图:

     上图可以看出步骤很多,下面将详细介绍几个重点的内容。

    调用初始化器

     在 SpringApplication 构建过程中设置的初始化器,从 spring.factories 取值的。执行的流程很简单,遍历执行,源码如下图:

     

    1. 将自定义的 ApplicationContextInitializer 放在 META-INF/spring.factories 中,在此时也是会被
    2. 调用。

    加载启动类,注入容器

    这一步是将主启动类加载到 IOC容器 中,作为后续自动配置的入口。
    在 SpringApplication 构建过程中将主启动类放置在 primarySources 这个集合中,此时的
    getAllSources() 即是从其中取值,如下图: 

     这里取出的就是主启动类,当然你的项目中可能不止一个,接下来就是将其加载到IOC容器中了,源码如下:

    跟着代码进去,其实主要逻辑都在 BeanDefinitionLoader.load() 方法,如下图:

    将主启动类加载到 beanDefinitionMap ,后续该启动类将作为开启自动配置化的入口。

    两次广播事件

     这一步涉及到了两次事件广播,分别是 ApplicationContextInitializedEvent 和
    ApplicationPreparedEvent ,对应的源码如下:

     5. 刷新容器

    刷新容器完全是 Spring 的功能了,比如初始化资源,初始化上下文广播器等。 

    1. protected void refresh(ApplicationContext applicationContext) {
    2.   Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    3.   //调用创建的容器applicationContext中的refresh()方法
    4.  ((AbstractApplicationContext)applicationContext).refresh();
    5. }
    6. public void refresh() throws BeansException, IllegalStateException {
    7.   synchronized (this.startupShutdownMonitor) {
    8.     /**
    9.     * 刷新上下文环境
    10.     */
    11.     prepareRefresh();
    12.     /**
    13.     * 初始化BeanFactory,解析XML,相当于之前的XmlBeanFactory的操作,
    14.     */
    15.     ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    16.     /**
    17.     * 为上下文准备BeanFactory,即对BeanFactory的各种功能进行填充,如常用的注解@Autowired
    18. @Qualifier
    19.     * 添加ApplicationContextAwareProcessor处理器
    20.     * 在依赖注入忽略实现*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
    21.     * 注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory
    22. 的实例注入进去
    23.     */
    24.     prepareBeanFactory(beanFactory);
    25.     try {
    26.       /**
    27.       * 提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
    28.       */
    29.       postProcessBeanFactory(beanFactory);
    30.       /**
    31.       * 激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通
    32. 的BeanFactoryPostProcessor
    33.       * 执行对应的postProcessBeanDefinitionRegistry方法 和 postProcessBeanFactory方法
    34.       */
    35.       invokeBeanFactoryPostProcessors(beanFactory);
    36.       /**
    37.       * 注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是
    38. BeanFactoryPostProcessor,注意两者的区别
    39.       * 注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法
    40.       */
    41.       registerBeanPostProcessors(beanFactory);
    42.       /**
    43.       * 初始化上下文中的资源文件,如国际化文件的处理等
    44.       */
    45.       initMessageSource();
    46.       /**
    47.       * 初始化上下文事件广播器,并放入applicatioEventMulticaster,如
    48. ApplicationEventPublisher
    49.       */
    50.       initApplicationEventMulticaster();
    51.       /**
    52.       * 给子类扩展初始化其他Bean
    53.       */
    54.       onRefresh();
    55.       /**
    56.       * 在所有bean中查找listener bean,然后注册到广播器中
    57.       */
    58.       registerListeners();
    59.       /**
    60.       * 设置转换器
    61.       * 注册一个默认的属性值解析器
    62.       * 冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理
    63.       * 初始化剩余的非惰性的bean,即初始化非延迟加载的bean
    64.       */
    65.       finishBeanFactoryInitialization(beanFactory);
    66.       /**
    67.       * 通过spring的事件发布机制发布ContextRefreshedEvent事件,以保证对应的监听器做进一步的
    68. 处理
    69.       * 即对那种在spring启动后需要处理的一些类,这些类实现了
    70. ApplicationListener
    71.       * 这里就是要触发这些类的执行(执行onApplicationEvent方法)
    72.       * 另外,spring的内置Event有ContextClosedEvent、ContextRefreshedEvent、
    73. ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
    74.       * 完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出
    75. ContextRefreshEvent通知其他人
    76.       */
    77.       finishRefresh();
    78.    }
    79. finally {
    80.  
    81.       resetCommonCaches();
    82.    }
    83.  }
    84. }

     6. IOC容器的后置处理

     一个扩展方法,源码如下:

     默认为空,如果有自定义需求可以重写,比如打印一些启动结束日志等。

    7. 发出结束执行的事件 

    同样是 EventPublishingRunListener 这个监听器,广播 ApplicationStartedEvent 事件。 

    1. 但是这里广播事件和前几次不同,并不是广播给 SpringApplication 中的监听器(在构建过程中从
    2. spring.factories 文件获取的监听器)。因此在 IOC容器 中注入的监听器(使用 @Component 等方
    3. 式注入的)也能够生效。前面几个事件只有在 spring.factories 文件中设置的监听器才会生效。

    跟着代码进入,可以看到 started() 方法源码如下:

     这里并没有用事件广播器 SimpleApplicationEventMulticaster 广播事件,而是使用
    ConfigurableApplicationContext 直接在 IOC容器 中发布事件。

    8. 执行Runners

    Spring Boot 提供了两种 Runner 让我们定制一些额外的操作,分别是 CommandLineRunner 和
    ApplicationRunner ,调用的源码如下: 

     跟进代码,其实真正调执行的是如下方法:

     逻辑,从 IOC容器 中获取,遍历调用。

    总结

    Spring Boot 启动流程细分了以上八个步骤,流程图如下: 

     总结

    Spring Boot 启动流程就介绍到这里了,需要重点理解 run() 方法执行的八个步骤以及事件、初始化器、监听器等组件的执行时间点。

  • 相关阅读:
    景区门票管理系统
    【微信开发第四章】SpringBoot实现微信H5支付
    vue - sass样式穿透实现
    《吐血整理》高级系列教程-吃透Fiddler抓包教程(22)-如何使用Fiddler生成Jmeter脚本-下篇
    模拟量信号干扰的主要原因及解决方案
    探索dbus-sensors: 从传感器读取到xyz.openbmc_project.Sensor接口的细致解析
    亚马逊云科技创新加速周:以数智化手段加速中国企业出海之旅
    C# 字符串操作
    Word控件Spire.Doc 【段落处理】教程(十四):如C#/VB.NET:删除 Word 中的空行
    c++ || 容器适配器
  • 原文地址:https://blog.csdn.net/wufaqidong1/article/details/126236638