Spring Boot - 生命周期与事件
原理会涉及源码,建议看视频学习会更容易理解,本内容不涉及源码解析,内容都学习自视频。
视频地址:https://www.bilibili.com/video/BV1Es4y1q7Bf/?p=59
,从 59 看到 64 集。
# 前言
Spring Boot 初始化的时候会有生命周期,在到达这些生命周期的时候,就会触发一个回调,我们可以实现这些回调,在这个生命周期的回调实现自己的业务,类似于 Vue 的生命周期。
除了生命周期,还有事件,生命周期是一个完整的阶段,而事件是这个阶段的一个行为。因此一个生命周期至少有一个事件以上。
我们可以监听某个生命周期,也可以监听某个事件,事件相比较生命周期更为具体。就像人有出生、学习、进入社会、结婚、生子、退休、死亡等生命周期,而这些生命周期会有很多的事件发生。
# 生命周期监听
了解 SpringBoot 初始化的生命周期,我们可以实现 SpringApplicationRunListener 接口的所有方法,这些方法就是 Spring Boot 的生命周期阶段回调。至于这些方法在哪里调用,可以在 SpringApplicaiton 的 run 方法里查看。
public class MyListener implements SpringApplicationRunListener {
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
System.out.println("项目正在启动");
SpringApplicationRunListener.super.starting(bootstrapContext);
}
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
System.out.println("环境准备完成");
SpringApplicationRunListener.super.environmentPrepared(bootstrapContext, environment);
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("容器加载前");
SpringApplicationRunListener.super.contextPrepared(context);
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("容器加载完成,但是 Bean 还没有刷新到容器");
SpringApplicationRunListener.super.contextLoaded(context);
}
@Override
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("环境启动完成,所有 Bean 创建完成");
SpringApplicationRunListener.super.started(context, timeTaken);
}
@Override
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("项目准备就绪");
SpringApplicationRunListener.super.ready(context, timeTaken);
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("项目启动失败");
SpringApplicationRunListener.super.failed(context, exception);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Spring Boot 初始化会经过三大步骤:
引导:利用 BootstrapContext 引导整个项目启动
- starting:应用开始,SpringApplication 的 run 方法一调用,只要有了 BootstrapContext 就执行
- environmentPrepared:环境准备好(把启动参数等绑定到环境变量中),但是 ioc 还没有创建【调一次】
启动项目
contextPrepared:ioc 容器创建并准备好,但是 sources(主配置类)没加载。并关闭引导上下文;组件都没创建【调一次】
contextLoaded:ioc 容器加载。主配置类加载进去了。但是 ioc 容器还没刷新(Bean 没创建)
截止以前,ioc 容器里面还没造 Bean
started:ioc 容器刷新了(所有bean造好了),但是 runner 没调用
ready:ioc 容器刷新了(所有bean造好了),所有 runner 调用完了
运行项目
以前步骤都正确执行,代表容器 Running。
# 事件触发时机
我们可以自定义类实现这些监听器,然后添加进去,这样在到达对应的事件后,就会自动触发方法,于是我们可以在这些方法里进行业务的处理。
# BootstrapRegistryInitializer
感知特定阶段:感知 引导初始化,创建引导上下文 bootstrapContext
的时候触发。
public class MyBootstrapRegistryInitializer implements BootstrapRegistryInitializer {
@Override
public void initialize(BootstrapRegistry registry) {
System.out.println("引导类初始化");
}
}
2
3
4
5
6
当时这样子还是没办法被 Spring Boot 使用,需要告诉将该类放到 Spring 容器里(现在处于初始化,所以不能使用 @Component
,这些注解是在初始化后才生效)。
配置在
resource/META-INF/spring.factories
文件下,这样 Spring Boot 就会扫描该文件的内容,放到容器中
org.springframework.boot.BootstrapRegistryInitializer=cn.youngkbt.boot.lift.MyBootstrapRegistryInitializer
通过主程序 application 的
addBootstrapRegistryInitializer()
添加
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MainApplication.class);
springApplication.addBootstrapRegistryInitializer(new MyBootstrapRegistryInitializer());
springApplication.run(args);
}
}
2
3
4
5
6
7
8
场景:进行密钥校对授权。
# ApplicationContextInitializer
感知特定阶段:感知 IOC 容器初始化。
这些事件的使用和 BootstrapRegistryInitializer 都一样。
自定义类实现 ApplicationContextInitializer
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContextInitializer");
}
}
2
3
4
5
6
配置在
resource/META-INF/spring.factories
文件下,这样 Spring Boot 就会扫描该文件的内容,放到容器中
org.springframework.context.ApplicationContextInitializer=cn.youngkbt.boot.lift.MyApplicationContextInitializer
通过主程序 application 的
addInitializers()
添加
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MainApplication.class);
springApplication.addInitializers(new MyApplicationContextInitializer());
springApplication.run(args);
}
}
2
3
4
5
6
7
8
# ApplicationListener
感知全阶段:基于事件机制,感知事件。一旦到了哪个阶段可以做别的事。
Spring Boot 除了生命周期,还有事件机制,每个生命周期都有对应的事件,而 ApplicationListener 可以监听这些事件的触发时机。
public class MyApplicationListener1 implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("事件都触发" + event.getSource());
}
}
2
3
4
5
6
这样每个事件触发的时候,都会执行 onApplicationEvent 方法。
如果我们想监听某个事件,如 ApplicationEnvironmentPreparedEvent 事件(Environment 准备好,但context 未创建),则:
public class MyApplicationListener2 implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
System.out.println("事件都触发" + event.getSource());
}
}
2
3
4
5
6
因此 ApplicationListener 不加泛型则每个事件都触发 onApplicationEvent 方法,加了泛型,则只监听泛型里的事件类。
当然这也需要注册到 Spring 容器才能使用:
配置在
resource/META-INF/spring.factories
文件下,这样 Spring Boot 就会扫描该文件的内容,放到容器中
org.springframework.context.ApplicationListener=cn.youngkbt.boot.lift.MyApplicationListener
通过主程序 application 的
addListeners()
添加
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MainApplication.class);
springApplication.addListeners(new MyApplicationListener());
springApplication.run(args);
}
}
2
3
4
5
6
7
8
# SpringApplicationRunListener
感知全阶段生命周期 + 各种阶段都能自定义操作,功能更完善。
使用方法、注册方法和上面一样。
# ApplicationRunner
感知特定阶段:感知应用就绪 Ready。卡死应用,就不会就绪。
# CommandLineRunner
感知特定阶段:感知应用就绪 Ready。卡死应用,就不会就绪
# 最佳实战
- 如果项目启动前做事:
BootstrapRegistryInitializer
和ApplicationContextInitializer
- 如果想要在项目启动完成后做事:
ApplicationRunner
和CommandLineRunner
- 如果要干涉生命周期做事:
SpringApplicationRunListener
- 如果想要用事件机制:
ApplicationListener
# 事件完整触发流程
9 大事件触发顺序 & 时机
ApplicationStartingEvent
:应用启动但未做任何事情, 除过注册 LKisteners 和 Initializers.ApplicationEnvironmentPreparedEvent
:Environment 准备好,但 Context 未创建ApplicationContextInitializedEvent
: ApplicationContext 准备好,ApplicationContextInitializers 调用,但是任何 Bean 未加载ApplicationPreparedEvent
: 容器刷新之前,Bean 定义信息加载ApplicationStartedEvent
: 容器刷新完成,Runner 未调用
以下就开始插入了 探针机制,搭配云原生使用:
AvailabilityChangeEvent
:LivenessState.CORRECT
应用存活,这是 存活探针ApplicationReadyEvent
: 任何 Runner 被调用AvailabilityChangeEvent
:ReadinessState.ACCEPTING_TRAFFIC
是 就绪探针,代表此时可以接请求ApplicationFailedEvent
:启动出错
存活探针:感知应用是否 存活了,可能植物状态,虽然活着但是不能处理请求。
就绪探针:应用是否 就绪 了,能响应请求,说明确实活的比较好。
上面 9 大事件是 Spring Boot 内置的。在项目启动后,我们也可以自定义事件使用,具体看 Spring Boot - 事件驱动 的使用方式。