学习方法之少废话: 吹牛, 装逼, 叫大哥.
作者: A 哥(YourBatman)
公众号: BAT 的乌托邦(ID:BAT-utopia)
文末是否有彩蛋: 有
目录
前言
正文
生命周期事件流程图
版本说明:
SpringApplicationEvent
ApplicationStartingEvent: 开始启动中
完成的大事记
监听此事件的监听器们
ApplicationEnvironmentPreparedEvent: 环境已准备好
完成的大事记
监听此事件的监听器们
ApplicationContextInitializedEvent: 上下文已实例化
完成的大事记
监听此事件的监听器们
ApplicationPreparedEvent: 上下文已准备好
完成的大事记
监听此事件的监听器们
ApplicationStartedEvent: 应用成功启动
完成的大事记
监听此事件的监听器们
ApplicationReadyEvent: 应用已准备好
完成的大事记
监听此事件的监听器们
异常情况
ApplicationFailedEvent: 应用启动失败
完成的大事记
监听此事件的监听器们
总结
前言
各位小伙伴大家好, 我是 A 哥. 本文属总结性文章, 对总览 Spring Boot 生命周期很是重要, 建议点在看, 转发 "造福" 更多小伙伴.
我最近不是在写 Spring Cloud 深度剖析的相关专栏麽, 最近有收到小伙伴发过来一些问题, 通过这段时间收集到的反馈, 总结了一下有一个问题非常集中: 那便是对 Spring Boot 应用 SpringApplication 的生命周期, 事件的理解. 有句话我不是经常挂嘴边说的麽, 你对 Spring Framework 有多了解决定了你对 Spring Boot 有多了解, 你对 Spring Boot 的了解深度又会制约你去了解 Spring Cloud, 一环扣一环. 因此此问题反馈比较集中是在清理之中的~
为何在 Spring Boot 中生命周期事件机制如此重要? 缘由很简单: Spring Cloud 父容器是由该生命周期事件机制来驱动的, 而它仅仅是一个典型代表. Spring Cloud 构建在 Spring Boot 之上, 它在此基础上构建并添加了一些 "Cloud" 功能. 应用程序事件 ApplicationEvent 以及监听 ApplicationListener 是 Spring Framework 提供的扩展点, Spring Boot 对此扩展点利用得非常充分和深入, 并且还衍生出了非常多 "子" 事件类型, 甚至自成体系. 从 ApplicationEvent 衍生出来的子事件类型非常多, 例如 JobExecutionEvent,RSocketServerInitializedEvent,AuditApplicationEvent...
本文并不会对每个子事件分别介绍(也并无必要), 而是集中火力主攻 Spring Boot 最为重要的一套事件机制: SpringApplication 生命周期的事件体系.
正文
本文将以 SpringApplication 的启动流程 / 生命周期各时期发出的 Event 事件为主线, 结合每个生命周期内完成的大事记介绍, 真正实现一文让你总览 Spring Boot 的全貌, 这对你深入理解 Spring Boot, 以及整合进 Spring Cloud 都将非常重要.
为表诚意, 本文一开始便把 SpringApplication 生命周期事件流程图附上, 然后再精细化讲解各个事件的详情.
话外音: 赶时间的小伙伴可以拿图走人, 但不建议白嫖哟
生命周期事件流程图
版本说明:
由于不同版本, 类路径下存在不同包时结果会存在差异, 不指明版本的文章都是不够负责任的. 因此对导包 / 版本情况作出如下说明:
Spring Boot:2.2.2.RELEASE. 有且仅导入
spring-boot-starter-web
和
spring-boot-starter-actuator
Spring Cloud:Hoxton.SR1. 有且仅导入
- spring-cloud-context
- (注意: 并非 spring-cloud-starter, 并不含有 spring-cloud-commons 哦)
总的来说: 本例导包是非常非常 "干净" 的, 这样在流程上才更有说服力嘛~
SpringApplicationEvent
它是和 SpringApplication 生命周期有关的所有事件的父类,@since 1.0.0.
- public abstract class SpringApplicationEvent extends ApplicationEvent {
- private final String[] args;
- public SpringApplicationEvent(SpringApplication application, String[] args) {
- super(application);
- this.args = args;
- }
- public SpringApplication getSpringApplication() {
- return (SpringApplication) getSource();
- }
- public final String[] getArgs() {
- return this.args;
- }
- }
它是抽象类, 扩展自 Spring Framwork 的 ApplicationEvent, 确保了事件和应用实体 SpringApplication 产生关联(当然还有 String[] args). 它有如下实现子类(7 个):
每个事件都代表着 SpringApplication 不同生命周期所处的位置, 下面分别进行讲解.
ApplicationStartingEvent: 开始启动中
@since 1.5.0, 并非 1.0.0 就有的哦. 不过现在几乎没有人用 1.5 以下的版本了, 所以可当它是标准事件.
完成的大事记
SpringApplication 实例已实例化:
new SpringApplication(primarySources)
它在实例化阶段完成了如下几件 "大" 事:
推断出应用类型 webApplicationType,main 方法所在类
给字段 initializers 赋值: 拿到 SPI 方式配置的
ApplicationContextInitializer
上下文初始化器
给字段 listeners 赋值: 拿到 SPI 方式配置的
ApplicationListener
应用监听器
注意: 在此阶段 (早期阶段) 不要过多地使用它的内部状态, 因为它可能在生命周期的后期被修改(话外音: 使用时需谨慎)
此时,
SpringApplicationRunListener
已实例化: 它通过 SPI 方式指定
- org.springframework.boot.SpringApplicationRunListener=org.springframework.boot.context.event.EventPublishingRunListener
- .
若你有自己的运行时应用监听器, 使用相同方式配置上即可, 均会生效
由于
EventPublishingRunListener
已经实例化了, 因此在后续的事件发送中, 均能够触发对应的监听器的执行
发送 ApplicationStartingEvent 事件, 触发对应的监听器的执行
监听此事件的监听器们
默认情况下, 有 4 个监听器监听 ApplicationStartingEvent 事件:
LoggingApplicationListener
:@since 2.0.0. 对日志系统抽象 LoggingSystem 执行实例化以及初始化之前的操作, 默认使用的是基于 Logback 的
- LogbackLoggingSystem
- BackgroundPreinitializer
: 启动一个后台进行对一些类进行预热. 如
ValidationInitializer,JacksonInitializer...
, 因为这些组件有第一次惩罚的特点(并且首次初始化均还比较耗时), 所以使用后台线程先预热效果更佳
DelegatingApplicationListener
: 它监听的是 ApplicationEvent, 而实际上只会
ApplicationEnvironmentPreparedEvent
到达时生效, 所以此处忽略
LiquibaseServiceLocatorApplicationListener
: 略
总结: 此事件节点结束时, SpringApplication 完成了一些实例化相关的动作: 本实例实例化, 本实例属性赋值, 日志系统实例化等.
ApplicationEnvironmentPreparedEvent: 环境已准备好
@since 1.0.0. 该事件节点是最为重要的一个节点之一, 因为对于 Spring 应用来说, 环境抽象 Enviroment 简直太重要了, 它是最为基础的元数据, 决定着程序的构建和走向, 所以构建的时机是比较早的.
完成的大事记
封装命令行参数 (main 方法的 args) 到
ApplicationArguments
里面
创建出一个环境抽象实例
ConfigurableEnvironment
的实现类, 并且填入值: Profiles 配置和 Properties 属性, 默认内容如下(注意, 这只是初始状态, 后面还会改变, 添加属性源, 实际见最后的截图):
发送 ApplicationEnvironmentPreparedEvent 事件, 触发对应的监听器的执行
对环境抽象 Enviroment 的填值, 均是由监听此事件的监听器去完成, 见下面的监听器详解
bindToSpringApplication(environment)
: 把环境属性中
spring.main.xxx = xxx
绑定到当前的 SpringApplication 实例属性上, 如常用的
spring.main.allow-bean-definition-overriding=true
会被绑定到当前 SpringApplication 实例的对应属性上
监听此事件的监听器们
默认情况下, 有 9 个监听器监听 ApplicationEnvironmentPreparedEvent 事件:
BootstrapApplicationListener
: 来自 SC. 优先级最高, 用于启动 / 创建 Spring Cloud 的应用上下文. 需要注意的是: 到此时 SB 的上下文 ApplicationContext 还并没有创建哦. 这个流程 "嵌套" 特别像 Bean 初始化流程: 初始化 Bean A 时, 遇到了 Bean B, 就需要先去完成 Bean B 的初始化, 再回头来继续完成 Bean A 的步骤.
说明: 在创建 SC 的应用的时候, 使用的也是
SpringApplication#run()
完成的(非 Web), 因此也会走下一整套 SpringApplication 的生命周期逻辑, 所以请你务必区分.
特别是这种 case 会让 "绝大多数" 初始化器, 监听器等执行多次, 若你有那种只需要执行一次的需求(比如只想让 SB 容器生命周期内执行, SC 生命周期不执行), 请务必自行处理, 否则会被执行多次而带来不可预知的结果
SC 应用上下文读取的外部化配置文件名默认是: Bootstrap, 使用的也是
ConfigFileApplicationListener
完成的加载 / 解析
LoggingSystemShutdownListener
- spring.output.ansi.console-available = true/false
- LoggingApplicationListener
- SERVLET -> AnnotationConfigServletWebServerApplicationContext
- REACTIVE -> AnnotationConfigReactiveWebServerApplicationContext
- context.initializer.classes = xxx,xxx
- SharedMetadataReaderFactoryContextInitializer
- "springBootLoggingSystem" -> loggingSystem
- "springBootLogFile" -> logFile
- "springBootLoggerGroups" -> loggerGroups
- BackgroundPreinitializer
来源: https://www.cnblogs.com/yourbatman/p/13257999.html