您现在的位置是:首页 >学无止境 >SpringBoot启动过程源码解析网站首页学无止境

SpringBoot启动过程源码解析

野心与梦 2024-06-17 11:18:51
简介SpringBoot启动过程源码解析

SpringBoot启动过程源码解析

构造SpringApplication对象

1、推测web应用类型

  1. 应用类型为WebApplicationType.REACTIVE
    1. 项目依赖中存在org.springframework.web.reactive.DispatcherHandler
    2. 项目依赖中不存在org.springframework.web.servlet.DispatcherServlet
  2. WebApplicationType.NONE
    1. 项目依赖中不存在org.springframework.web.reactive.DispatcherHandler
    2. 项目依赖中不存在org.springframework.web.servlet.DispatcherServlet
  3. WebApplicationType.SERVLET
    1. 项目依赖中不存在org.springframework.web.reactive.DispatcherHandler
    2. 项目依赖中存在org.springframework.web.servlet.DispatcherServlet

2、获取BootstrapRegistryInitializer对象

  1. 从"META-INF/spring.factories"中读取key为BootstrapRegistryInitializer类型的扩展点,并实例化出对应扩展点对象
  2. BootstrapRegistryInitializer的作用是可以初始化BootstrapRegistry,DefaultBootstrapContext对象就是一个BootstrapRegistry,可以用来注册一些对象,这些对象可以用在从SpringBoot启动到Spring容器初始化完成的过程中

我的理解:没有Spring容器之前就利用BootstrapRegistry来共享一些对象,有了Spring容器之后就利用Spring容器来共享一些对象

3、获取ApplicationContextInitializer对象

  1. 从"META-INF/spring.factories"中读取key为ApplicationContextInitializer类型的扩展点,并实例化出对应扩展点对象
  2. 顾名思义,ApplicationContextInitializer是用来初始化Spring容器ApplicationContext对象的,比如可以利用ApplicationContextInitializer来向Spring容器中添加ApplicationListener

4、获取ApplicationListener对象

  1. 从"META-INF/spring.factories"中读取key为ApplicationListener类型的扩展点,并实例化出对应扩展点对象
  2. ApplicationListener是Spring中的监听器

5、推测出Main类(main()方法所在的类)

没什么具体的作用,逻辑是根据当前线程的调用栈来判断main()方法在哪个类,哪个类就是Main类
run(String… args)方法

  1. 创建DefaultBootstrapContext对象
  2. 利用BootstrapRegistryInitializer初始化DefaultBootstrapContext对象
  3. 获取SpringApplicationRunListeners

5、触发SpringApplicationRunListener的starting()

默认情况下SpringBoot提供了一个EventPublishingRunListener,它实现SpringApplicationRunListener接口,默认情况下会利用EventPublishingRunListener发布一个ApplicationContextInitializedEvent事件,程序员可以通过定义ApplicationListener来消费这个事件

6、创建Environment对象

Environment对象表示环境变量,该对象内部主要包含了:

  1. 当前操作系统的环境变量
  2. JVM的一些配置信息
  3. -D方式所配置的JVM环境变量

7、触发SpringApplicationRunListener的environmentPrepared()

默认情况下会利用EventPublishingRunListener发布一个ApplicationEnvironmentPreparedEvent事件,程序员可以通过定义ApplicationListener来消费这个事件,比如默认情况下会有一个EnvironmentPostProcessorApplicationListener来消费这个事件,而这个ApplicationListener接收到这个事件之后,就会解析application.properties、application.yml文件,并添加到Environment对象中去。

8、打印Banner

9、创建Spring容器对象(ApplicationContext)

会利用ApplicationContextFactory.DEFAULT,根据应用类型创建对应的Spring容器。
ApplicationContextFactory.DEFAULT为:

ApplicationContextFactory DEFAULT = (webApplicationType) -> {
    try {
        switch (webApplicationType) {
            case SERVLET:
                return new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE:
                return new AnnotationConfigReactiveWebServerApplicationContext();
            default:
                return new AnnotationConfigApplicationContext();
        }
    }
    catch (Exception ex) {
        throw new IllegalStateException("Unable create a default ApplicationContext instance, "
                                        + "you may need a custom ApplicationContextFactory", ex);
    }
};
  1. 应用类型为SERVLET,则对应AnnotationConfigServletWebServerApplicationContext
  2. 应用类型为REACTIVE,则对应AnnotationConfigReactiveWebServerApplicationContext
  3. 应用类型为普通类型,则对应AnnotationConfigApplicationContext

10、利用ApplicationContextInitializer初始化Spring容器对象

默认情况下SpringBoot提供了多个ApplicationContextInitializer,其中比较重要的有ConditionEvaluationReportLoggingListener实现了ApplicationContextInitializer接口,在它的initialize()方法:

  1. 将Spring容器赋值给它的applicationContext属性
  2. 并且往Spring容器中添加一个ConditionEvaluationReportListener(ConditionEvaluationReportLoggingListener的内部类),它是一个ApplicationListener
  3. 并生成一个ConditionEvaluationReport对象赋值给它的report属性

ConditionEvaluationReportListener会负责接收ContextRefreshedEvent事件,也就是Spring容器一旦启动完毕就会触发ContextRefreshedEvent,ConditionEvaluationReportListener就会打印自动配置类的条件评估报告。

11、触发SpringApplicationRunListener的contextPrepared()

默认情况下会利用EventPublishingRunListener发布一个ApplicationContextInitializedEvent事件,默认情况下暂时没有ApplicationListener消费了这个事件

12、调用DefaultBootstrapContext对象的close()

没什么特殊的,忽略

13、将启动类作为配置类注册到Spring容器中(load()方法)

将SpringApplication.run(MyApplication.class);中传入进来的类,比如MyApplication.class,作为Spring容器的配置类

14、触发SpringApplicationRunListener的contextLoaded()

默认情况下会利用EventPublishingRunListener发布一个ApplicationPreparedEvent事件

15、刷新Spring容器

调用Spring容器的refresh()方法,结合第9、13步,相当于执行了这样一个流程:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(MyApplication.class)
applicationContext.refresh()

16、触发SpringApplicationRunListener的started()

发布ApplicationStartedEvent事件和AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示状态变更状态,变更后的状态为LivenessState.CORRECT
LivenessState枚举有两个值:

  1. CORRECT:表示当前应用正常运行中
  2. BROKEN:表示当前应用还在运行,但是内部出现问题,暂时还没发现哪里用到了

17、调用ApplicationRunner和CommandLineRunner

  1. 获取Spring容器中的ApplicationRunner类型的Bean
  2. 获取Spring容器中的CommandLineRunner类型的Bean
  3. 执行它们的run()

18、触发SpringApplicationRunListener的ready()

发布ApplicationReadyEvent事件和AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示状态变更状态,变更后的状态为ReadinessState.ACCEPTING_TRAFFIC
ReadinessState枚举有两个值:

  1. ACCEPTING_TRAFFIC:表示当前应用准备接收请求
  2. REFUSING_TRAFFIC:表示当前应用拒绝接收请求,比如Tomcat关闭时,就会发布AvailabilityChangeEvent事件,并且状态为REFUSING_TRAFFIC

19、启动异常了就触发SpringApplicationRunListener的failed()

发布ApplicationFailedEvent事件

配置文件解析

  1. 创建Enviroment对象

添加servlet, servlet容器、操作系统、JVM环境变量
比如: java-jar-Dserver.port=8887 springbootdemo.jar

  1. servletConfiglnitParams
  2. servletContextInitParams
  3. systemProperties
  4. systemEnvironment
  1. 添加DefaultProperties

添加默认的,通过SpringApplication的setDefaultProperties()方法设置的

  1. servletConfigInitParams
  2. servletContextInitParams
  3. systemProperties
  4. systemEnvironment
  5. defaultProperties
  1. 把命令行中的参数封装成PropertySource,添加到Environment对象中

添加运行时命今行中设置的参数比如: java-jar springbootdemo.jar–server.port=8888

  1. commandLineArgs
  2. servletConfiglnitParams
  3. servletContextInitParams
  4. systemProperties
  5. systemEnvironment
  6. defaultProperties
  1. 添加一个RandomValuePropertySource

添加一个能产生随机数的

  1. commandLineArgs
  2. servletConfiglnitParams
  3. servletContextInitParams
  4. systemProperties
  5. systemEnvironment
  6. random
  7. defaultProperties
  1. 从已有Environment中读取,spring.application.json配置

添加-Dspring.application.json参数配置的

  1. commandLineArgs
  2. spring.application.json
  3. servletConfiglnitParams
  4. servletContextInitParams
  5. systemProperties
  6. systemEnvironment
  7. random
  8. defaultProperties
  1. 如果当前是在CLOUD_FOUNDRY上

添加云平台相关的

  1. commandLineArgs
  2. vcap
  3. spring.application.json•
  4. servletConfigInitParams
  5. servletContextInitParams
  6. systemProperties
  7. systemEnvironment
  8. random
  9. defaultProperties
  1. 配置文件解析
  1. spring.config.location (如果指定了,默认的会失效)
  2. spring.config.import (在默认的基础上新增)
  3. spring.config.additional-location (在默认的基础上新增)

获取这三个参数指定的路径,查找配置文件的目录,顺序为

  1. classpath:/xxx/
  2. file:./
  3. file:./config
  4. file:./config/*/
  5. classpath:/
  6. classpath:/config/

注:properties的优先级高于yml

  1. 解析上述所有路径,解析真正存在的文件路径

注意,这里并不是解析每个文件生成PropertySource,添加到Environment对象中,这里只是先解析出PropertySource对象,并按顺序放到一个集合里面

  1. 使用spring.profiles.active

从以上所有配置中找spring.profiles.active,找出对·应的值,然后按指定的值再去找对应的文件,比如dev

  1. 配置文件生效前提

在每个配置文件中可以通过
spring.config.activate.on-profile=dev
spring.config.activate.on-cloudPlatform=CLOUD_FOUNDRY
来指定配置文件生效的前提

  1. 匹配配置

按当前的支持的profiles和cloudPlatform来过滤配置文件中没有配的,以及匹配的则会胜出

  1. 生成配置

把最后胜出的配置文件生成对应的PropertySource添加,到Environment中

SpringBoot完整的配置优先级

优先级由低到高(注意和我写的顺序是反的,我写的是由高到底)

  1. Default properties (specified by settingSpringApplication.setDefaultProperties).
  2. @PropertySource annotations on your @Configuration classes. Please note that such property sources are not added to the Environment until the application context is being refreshed. This is too late to configure certain properties such as logging.* and spring.main.* which are read before refresh begins.
  3. Config data (such asapplication.properties files).
  4. ARandomValuePropertySource that has properties only in random.*.
  5. OS environment variables.
  6. Java System properties (System.getProperties()).
  7. JNDI attributes from java:comp/env. 不管它
  8. ServletContext init parameters.
  9. ServletConfig init parameters.
  10. Properties fromSPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
  11. Command line arguments.
  12. properties attribute on your tests. Available on @SpringBootTest and the test annotations for testing a particular slice of your application.
  13. @TestPropertySource annotations on your tests.
  14. Devtools global settings properties in the $HOME/.config/spring-boot directory when devtools is active.
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。