SpringBoot和SpringCloud启动流程

写在前面:今天是母亲节,XiXi在这里衷心祝愿天下所有的母亲们,节日快乐,健康长寿,幸福安康!!

哎呀,终于休息啦!!!连上6天班,终于迎来了一天的喘息机会。今天和大家聊聊「SpringBoot」「SpringCloud」的启动流程。文章是XiXi通过浏览别人的博客,加之自己翻源码验证总结出来的,如有错误的地方,还请大家批评指正。

源码阅读版本:

  • SpringBoot:2.7.18
  • SpringCloud:2021.0.4.0

SpringBoot的启动流程

整体流程

SpringBoot和SpringCloud启动流程总的来说,SpringBoot的启动流程分为两个大阶段

  1. 「SpringApplication」的创建阶段

这一阶段主要是创建「SpringApplication」对象,为其属性赋值,主要有以下属性:

  • 「resourceLoader」:资源加载器
  • 「primarySources」:主配置类字节码列表
  • 「webApplicationType」:应用类型,主要有:「NONE」(非Web),「SERVLET」(Servlet的Web应用),「REACTIVE」(Reactive的Web应用)
  • 「bootstrapRegistryInitializers」「BootstrapRegistryInitializer」集合,用于初始化「DefaultBootstrapContext」
  • 「initializers:ApplicationContextInitializer」集合,用于初始化「Spring应用上下文(ApplicationContext)」
  • 「listeners:ApplicationListener」集合

  1. 「SpringApplication」「run方法」执行阶段

本阶段执行的任务有以下几点:

  • 创建「DefaultBootstrapContext」
  • 发布SpringBoot启动相关事件,触发有关「SpringApplicationRunListener」逻辑执行
  • 创建环境「ConfigurableEnvironment」
  • 创建、准备、刷新Spring应用上下文
  • 打印Banner
  • 调用「Runner」方法

SpringBoot和SpringCloud启动流程

SpringApplication的创建阶段

SpringBoot和SpringCloud启动流程
  1. 设置资源加载器「resourceLoader」
  2. 设置主要配置类字节码「primarySources」
  3. 推断并设置应用类型「webApplicationType」

三种应用类型

  • 「NONE」:非Web应用
  • 「SERVLET」:基于Servlet的Web应用
  • 「REACTIVE」:响应式的Web应用

推断的方式是:通过判断项目中是否存在相关字节码

  1. 通过SPI方式获取并设置「BootstrapRegistryInitializer」集合「bootstrapRegistryInitializers」

「BootstrapRegistryInitializer」集合用于「DefaultBootstrapContext」的初始化

  1. 通过SPI方式获取并设置「ApplicationContextInitializer」集合「initializers」

「ApplicationContextInitializer」集合用于「Spring应用上下文」的初始化

  1. 通过SPI方式获取并设置「ApplicationListener」集合「listeners」

「ApplicationListener」集合自然是给到「Spring应用上下文」

  1. 推断并设置主启动类字节码「mainApplicationClass」

主启动类或者说叫启动类的设置,意思是哪个类执行的「main()「方法。这里是通过创建一个」RuntimeException」对象,并通过其堆栈信息,获取执行**main()**方法的类

SpringBoot和SpringCloud启动流程

SpringBoot的SPI:会从项目中读取「spring.factories」文件,加载文件中的类SpringBoot和SpringCloud启动流程

SpringApplication的run方法执行阶段

SpringBoot和SpringCloud启动流程

  1. 创建并使用「bootstrapRegistryInitializers」初始化「DefaultBootstrapContext」
  2. 设置无界面属性「java.awt.headless(缺失显示屏、鼠标或者键盘)」
  3. 通过SPI方式获取「SpringApplicationRunListener」并组合成「SpringApplicationRunListeners」

SpringBoot和SpringCloud启动流程

  1. 调用所有SpringBoot监听器的starting(),「SpringApplicationRunListener#starting()」
  2. 创建参数对象「DefaultApplicationArguments」
  3. 准备环境「ConfigurableEnvironment」

** 「本阶段会额外触发」SpringApplicationRunListener**的1个方法

  • 「SpringBootRunListener#environmentPrepared()」

  1. 配置是否忽略BeanInfo
  2. 打印Banner
  3. 「创建」应用上下文「ConfigurableApplicationContext」
  4. 「准备」应用上下文「ConfigurableApplicationContext」

本阶段会运用上「ApplicationContextInitializer集合」(「initializers属性」) 本阶段会额外触发「SpringApplicationRunListener」另2个方法

  • 「SpringBootRunListener#contextPrepared()」
  • 「SpringBootRunListener#contextLoaded()」

  1. 「刷新」应用上下文「ConfigurableApplicationContext」
  2. 执行应用上下文「ConfigurableApplicationContext刷新后」动作
  3. 调用所有SpringBoot监听器的started(),「SpringApplicationRunListener#started()」
  4. 调用「ApplicationRunner」「CommandLineRunner」
  5. 调用所有SpringBoot监听器的ready(),「SpringApplicationRunListener#ready()」

这里跟着「run()方法」的代码,罗列了「run阶段」的所有步骤,这里只是总览,深入任何一个方法,都有不少流程。

下面我将「run阶段」「Spring应用上下文流程」「SpringBootRunListener流程」单独抽取出来,加深下记忆

run阶段中Spring应用上下文流程

SpringBoot和SpringCloud启动流程run阶段Spring应用上下文的流程

  1. 创建应用上下文
  2. 准备应用上下文
  3. 刷新应用上下文
  4. 执行应用上下文刷新后动作

run阶段SpringBootRunListener的流程

SpringBoot和SpringCloud启动流程关于「SpringBootRunListener」的执行流程分为下面几步:

  1. 通过SPI获取所有的「SpringBootRunListener」
  2. 执行所有「SpringBootRunListener#starting()」
  3. 执行所有「SpringBootRunListener#environmentPrepared()」
  4. 执行所有「SpringBootRunListener#contextPrepared()」
  5. 执行所有「SpringBootRunListener#contextLoaded()」
  6. 执行所有「SpringBootRunListener#started()」
  7. 执行所有「SpringBootRunListener#ready()」

代码层面,「SpringBootRunListener集合」全部被设置为「SpringBootRunListeners」对象的属性**,「调用」SpringBootRunListeners的方法,「本质是调用各个」SpringBootRunListener** 目前实现了「SpringBootRunListener」的,只有一个类**:EventPublishingRunListener,「这个类的处理逻辑是将事件发布给相关的」ApplicationContext**

SpringBoot的配置文件原理

配置属性优先级

  1. 属性在「application.properties」「application.yml」都存在时,以「application.properties」为准
  2. 属性在「application-{profile}.properties」「application.properties都存在时,「以」application-{profile}.properties」为准
  3. SpringCloud引入后,属性在「bootstrap.properties」「application.properties」都存在**,在应用程序运行阶段,以application.properties为准**

配置属性读取原理

配置属性的读取源码XiXi这边也没有翻的很透,目前只知道配置属性是何时读取?由哪个类进行读取?具体的读取逻辑大家还是自己研究吧

简述原理(SpringBoot2.7.18):

「application.properties」配置文件的读取是在「SpringBoot的run阶段」的准备环境(「prepareEnvironment方法」)时触发的,在「prepareEnvironment方法」中触发「SpringApplicationRunListener#environmentPrepared()」「SpringApplicationRunListener」的唯一实现类「EventPublishingRunListener」,会发布「ApplicationEnvironmentPreparedEvent事件」,这个事件会被「EnvironmentPostProcessorApplicationListener」获取,它将调用多个「EnvironmentPostProcessor」实现类,其中一个就是「ConfigDataEnvironmentPostProcessor」,由它完成「application.properties配置文件」读取

  1. 何时读取?

是由「SpringBoot的run阶段」「准备环境阶段,「发布」环境已准备事件」时触发的读取逻辑

  1. 由哪个类读取?

  • SpringBoot2.4之前:ConfigFileApplicationListener
  • SpringBoot2.4及之后:ConfigDataEnvironmentPostProcessor

SpringCloud的启动流程

简述Spring Cloud Commons

SpringBoot和SpringCloud启动流程

在讲SpringCloud的启动流程之前,我们需要先了解Spring官网的「Spring Cloud Commons」项目。这个项目是SpringCloud项目的基石。在SpringBoot基础上引入「Bootstrap Context」、定义了一些抽象,在这些基础上,其他的一些SpringCloud组件才得以实现。所以,这里我们先明确几个要点:

主应用上下文:SpingBoot项目启动时返回的那个上下文,或者说我们Controller、Service等Bean所在的那个应用上下文

  • SpringCloud会额外启动一个「Bootstrap Context」(也是一个ApplicationContext)作为主应用上下文的父上下文
  • SpringCloud引入后,「bootstrap配置文件」的解析工作也是在「Bootstrap Context」中完成,并会合并到主应用上下文中

启动流程描述

流程描述中会有几个词,这里我先解释一下

  • 「主应用」:就是SpringBoot服务本来要启动的那个Spring应用上下文,或者说启动类main方法执行后启动的那个Spring应用上下文,或者说我们Controller、Service等Bean所在的那个应用上下文,
  • 「SpringApplication」:SpringBoot构建每个Spring应用上下文,都会创建一个「SpringApplication」来配置和启动进而完成一个Spring应用上下文的构建
  • 「Bootstrap Context」:是由于SpringCloud引入后,额外启动的一个Spring应用上下文,会作为主应用上下文的父级

SpringBoot和SpringCloud启动流程
  1. 程序启动,「主应用SpringApplication」进入构造阶段,并完成构造任务。
  2. 「主应用SpringApplication」进入run阶段,并执行到「prepareEnvironment方法」

「prepareEnvironment方法」会调用「SpringApplicationRunListeners#environmentPrepared方法。」最终是调用「EventPublishingRunListener#environmentPrepared」方法

  1. 「EventPublishingRunListener#environmentPrepared」会广播一个「ApplicationEnvironmentPreparedEvent」事件,并会调用相关「ApplicationListener」进行处理

「spring-cloud-context」依赖被引入后,「BootstrapApplicationListener」会被引入,并作为首个处理该事件的「ApplicationListener」进行自己的逻辑

  1. 「BootstrapApplicationListener逻辑:「首先会判断环境中有没有」bootstrap属性」(这个属性作为是否是「Bootstrap Context」启动的标识)

  • 若是首次启动,即「主应用SpringApplication」启动:「bootstrap属性不存在,「继续执行」BootstrapApplicationListener」逻辑
  • 若是「Bootstrap Context」启动:「bootstrap属性存在,「程序跳过」BootstrapApplicationListener」逻辑,继续下一个「ApplicationListener」

  1. 「BootstrapApplicationListener逻辑:「在」新的SpringApplication的环境」中放置「bootstrap属性」
  2. 「BootstrapApplicationListener逻辑」:构建一个「新的SpringApplication」,并会指定加载项目中存在的「BootstrapConfiguration」

  • 「BootstrapConfiguration」类:就是**@Configuration**配置类

  1. 「BootstrapApplicationListener逻辑」「新的SpringApplication」执行run方法

「新的SpringApplication」「主应用SpringApplication」一样执行run方法。不同点在于,「新的SpringApplication」也执行到「BootstrapApplicationListener」时,「会直接跳过」,因为在「第5步」时**,「在」新的SpringApplication环境「中放置了」bootstrap属性。**

  1. 「BootstrapApplicationListener逻辑:新的SpringApplication」的run方法执行完毕后,会返回一个「新的ApplicationContext」「Bootstrap Context」
  2. 「BootstrapApplicationListener逻辑:「将」第8步」返回的「Bootstrap Context」给到「主应用SpringApplication,「用来作为」主应用ApplicationContext」「父级应用上下文」
  3. 「BootstrapApplicationListener逻辑:「将」新SpringApplication的环境属性或Bean」合并应用到「主应用SpringApplication环境」
  4. 「BootstrapApplicationListener逻辑:「执行完毕」「继续回到」主应用SpringApplication」流程中的下一个「处理ApplicationEnvironmentPreparedEvent事件的监听器」执行
  5. 最后,「主应用SpringApplication」完成「run阶段执行」

开头的图片大家可能感觉有点绕,下面我又画了一张图,希望大家能明白我的意思

SpringBoot和SpringCloud启动流程

原理要点

  • SpringCloud引入后,会引入一个「BootstrapApplicationListener,「它是一个」ApplicationListener」的实现

「spring-cloud-context」依赖引入,用来监听「ApplicationEnvironmentPreparedEvent」

  • 「ApplicationEnvironmentPreparedEvent」是在「SpringApplication的run阶段的环境准备」时发布
  • 「BootstrapApplicationListener」会启动「Bootstrap Context」并作为主应用上下文的父级
  • 环境对象中是否有「bootstrap属性」,将作为**主应用上下文(无)「和」Bootstrap Context(有)**的区别标记
  • SpringCloud引入后,会加载「BootstrapConfiguration」配置类(就是配置类),这些配置类会在「Bootstrap Context」中被执行
  • bootstrap配置文件是在「Bootstrap Context」中解析并会合并到「主应用上下的环境中」

在应用程序下,applicaiton配置文件中的属性会覆盖bootstrap配置文件的属性,即applicaiton配置文件优先级高

总结

SpringBoot和SpringCloud的启动流程就分析到这里,大致流程XiXi这边是说完了,但是有很多细节上的东西,XiXi感觉很无力,没时间也没精力去深挖,而且个人感觉挖出来了用处也不大。


原文始发于微信公众号(溪溪技术笔记):SpringBoot和SpringCloud启动流程

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/289801.html

(0)
葫芦侠五楼的头像葫芦侠五楼

相关推荐

发表回复

登录后才能评论
极客之家——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!