❝
写在前面:今天是母亲节,XiXi在这里衷心祝愿天下所有的母亲们,节日快乐,健康长寿,幸福安康!!
❞
哎呀,终于休息啦!!!连上6天班,终于迎来了一天的喘息机会。今天和大家聊聊「SpringBoot」和「SpringCloud」的启动流程。文章是XiXi通过浏览别人的博客,加之自己翻源码验证总结出来的,如有错误的地方,还请大家批评指正。
❝
源码阅读版本:
SpringBoot:2.7.18 SpringCloud:2021.0.4.0 ❞
SpringBoot的启动流程
整体流程
-
「SpringApplication」的创建阶段
❝
这一阶段主要是创建「SpringApplication」对象,为其属性赋值,主要有以下属性:
「resourceLoader」:资源加载器 「primarySources」:主配置类字节码列表 「webApplicationType」:应用类型,主要有:「NONE」(非Web),「SERVLET」(Servlet的Web应用),「REACTIVE」(Reactive的Web应用) 「bootstrapRegistryInitializers」:「BootstrapRegistryInitializer」集合,用于初始化「DefaultBootstrapContext」 「initializers:ApplicationContextInitializer」集合,用于初始化「Spring应用上下文(ApplicationContext)」 「listeners:ApplicationListener」集合 ❞
-
「SpringApplication」的「run方法」执行阶段
❝
本阶段执行的任务有以下几点:
创建「DefaultBootstrapContext」 发布SpringBoot启动相关事件,触发有关「SpringApplicationRunListener」逻辑执行 创建环境「ConfigurableEnvironment」 创建、准备、刷新Spring应用上下文 打印Banner 调用「Runner」方法 ❞

SpringApplication的创建阶段

-
设置资源加载器「resourceLoader」 -
设置主要配置类字节码「primarySources」 -
推断并设置应用类型「webApplicationType」
❝
三种应用类型
「NONE」:非Web应用 「SERVLET」:基于Servlet的Web应用 「REACTIVE」:响应式的Web应用 推断的方式是:通过判断项目中是否存在相关字节码
❞
-
通过SPI方式获取并设置「BootstrapRegistryInitializer」集合「bootstrapRegistryInitializers」
❝
「BootstrapRegistryInitializer」集合用于「DefaultBootstrapContext」的初始化
❞
-
通过SPI方式获取并设置「ApplicationContextInitializer」集合「initializers」
❝
「ApplicationContextInitializer」集合用于「Spring应用上下文」的初始化
❞
-
通过SPI方式获取并设置「ApplicationListener」集合「listeners」
❝
「ApplicationListener」集合自然是给到「Spring应用上下文」中
❞
-
推断并设置主启动类字节码「mainApplicationClass」
❝
主启动类或者说叫启动类的设置,意思是哪个类执行的「main()「方法。这里是通过创建一个」RuntimeException」对象,并通过其堆栈信息,获取执行**main()**方法的类
❞

❝
SpringBoot的SPI:会从项目中读取「spring.factories」文件,加载文件中的类
❞
SpringApplication的run方法执行阶段

-
创建并使用「bootstrapRegistryInitializers」初始化「DefaultBootstrapContext」 -
设置无界面属性「java.awt.headless(缺失显示屏、鼠标或者键盘)」 -
通过SPI方式获取「SpringApplicationRunListener」并组合成「SpringApplicationRunListeners」
❝
❞
-
调用所有SpringBoot监听器的starting(),「SpringApplicationRunListener#starting()」 -
创建参数对象「DefaultApplicationArguments」 -
准备环境「ConfigurableEnvironment」
❝
** 「本阶段会额外触发」SpringApplicationRunListener**的1个方法
「SpringBootRunListener#environmentPrepared()」 ❞
-
配置是否忽略BeanInfo -
打印Banner -
「创建」应用上下文「ConfigurableApplicationContext」 -
「准备」应用上下文「ConfigurableApplicationContext」
❝
本阶段会运用上「ApplicationContextInitializer集合」(「initializers属性」) 本阶段会额外触发「SpringApplicationRunListener」另2个方法
「SpringBootRunListener#contextPrepared()」 「SpringBootRunListener#contextLoaded()」 ❞
-
「刷新」应用上下文「ConfigurableApplicationContext」 -
执行应用上下文「ConfigurableApplicationContext刷新后」动作 -
调用所有SpringBoot监听器的started(),「SpringApplicationRunListener#started()」 -
调用「ApplicationRunner」和「CommandLineRunner」 -
调用所有SpringBoot监听器的ready(),「SpringApplicationRunListener#ready()」
这里跟着「run()方法」的代码,罗列了「run阶段」的所有步骤,这里只是总览,深入任何一个方法,都有不少流程。
❝
下面我将「run阶段」中「Spring应用上下文流程」和「SpringBootRunListener流程」单独抽取出来,加深下记忆
❞
run阶段中Spring应用上下文流程
-
创建应用上下文 -
准备应用上下文 -
刷新应用上下文 -
执行应用上下文刷新后动作
run阶段SpringBootRunListener的流程
关于「SpringBootRunListener」的执行流程分为下面几步:
-
通过SPI获取所有的「SpringBootRunListener」 -
执行所有「SpringBootRunListener#starting()」 -
执行所有「SpringBootRunListener#environmentPrepared()」 -
执行所有「SpringBootRunListener#contextPrepared()」 -
执行所有「SpringBootRunListener#contextLoaded()」 -
执行所有「SpringBootRunListener#started()」 -
执行所有「SpringBootRunListener#ready()」
❝
代码层面,「SpringBootRunListener集合」全部被设置为「SpringBootRunListeners」对象的属性**,「调用」SpringBootRunListeners的方法,「本质是调用各个」SpringBootRunListener** 目前实现了「SpringBootRunListener」的,只有一个类**:EventPublishingRunListener,「这个类的处理逻辑是将事件发布给相关的」ApplicationContext**
❞
SpringBoot的配置文件原理
配置属性优先级
-
属性在「application.properties」和「application.yml」都存在时,以「application.properties」为准 -
属性在「application-{profile}.properties」和「application.properties都存在时,「以」application-{profile}.properties」为准 -
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配置文件」读取
❞
-
何时读取?
❝
是由「SpringBoot的run阶段」的「准备环境阶段,「发布」环境已准备事件」时触发的读取逻辑
❞
-
由哪个类读取?
❝
SpringBoot2.4之前: ConfigFileApplicationListener
SpringBoot2.4及之后: ConfigDataEnvironmentPostProcessor
❞
SpringCloud的启动流程
简述Spring Cloud Commons
❝
❞
在讲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应用上下文,会作为主应用上下文的父级 ❞

-
程序启动,「主应用SpringApplication」进入构造阶段,并完成构造任务。 -
「主应用SpringApplication」进入run阶段,并执行到「prepareEnvironment方法」
❝
「prepareEnvironment方法」会调用「SpringApplicationRunListeners#environmentPrepared方法。」最终是调用「EventPublishingRunListener#environmentPrepared」方法
❞
-
「EventPublishingRunListener#environmentPrepared」会广播一个「ApplicationEnvironmentPreparedEvent」事件,并会调用相关「ApplicationListener」进行处理
❝
在「spring-cloud-context」依赖被引入后,「BootstrapApplicationListener」会被引入,并作为首个处理该事件的「ApplicationListener」进行自己的逻辑
❞
-
「BootstrapApplicationListener逻辑:「首先会判断环境中有没有」bootstrap属性」(这个属性作为是否是「Bootstrap Context」启动的标识)
❝
若是首次启动,即「主应用SpringApplication」启动:「bootstrap属性不存在,「继续执行」BootstrapApplicationListener」逻辑 若是「Bootstrap Context」启动:「bootstrap属性存在,「程序跳过」BootstrapApplicationListener」逻辑,继续下一个「ApplicationListener」 ❞
-
「BootstrapApplicationListener逻辑:「在」新的SpringApplication的环境」中放置「bootstrap属性」 -
「BootstrapApplicationListener逻辑」:构建一个「新的SpringApplication」,并会指定加载项目中存在的「BootstrapConfiguration」类
❝
「BootstrapConfiguration」类:就是**@Configuration**配置类 ❞
-
「BootstrapApplicationListener逻辑」:「新的SpringApplication」执行run方法
❝
「新的SpringApplication」和「主应用SpringApplication」一样执行run方法。不同点在于,「新的SpringApplication」也执行到「BootstrapApplicationListener」时,「会直接跳过」,因为在「第5步」时**,「在」新的SpringApplication环境「中放置了」bootstrap属性。**
❞
-
「BootstrapApplicationListener逻辑:新的SpringApplication」的run方法执行完毕后,会返回一个「新的ApplicationContext」(「Bootstrap Context」) -
「BootstrapApplicationListener逻辑:「将」第8步」返回的「Bootstrap Context」给到「主应用SpringApplication,「用来作为」主应用ApplicationContext」的「父级应用上下文」 -
「BootstrapApplicationListener逻辑:「将」新SpringApplication的环境属性或Bean」合并应用到「主应用SpringApplication环境」中 -
「BootstrapApplicationListener逻辑:「执行完毕」,「继续回到」主应用SpringApplication」流程中的下一个「处理ApplicationEnvironmentPreparedEvent事件的监听器」执行 -
最后,「主应用SpringApplication」完成「run阶段执行」
❝
开头的图片大家可能感觉有点绕,下面我又画了一张图,希望大家能明白我的意思
❞

原理要点
-
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