事件和监听器
生命周期监听
场景:监听应用的生命周期
监听器-SpringApplicationRunListener
- 自定义
SpringApplicationRunListener
来监听事件;
1.1. 编写SpringApplicationRunListener
实现类
1.2. 在META-INF/spring.factories
中配置org.springframework.boot.SpringApplicationRunListener=自己的Listener
,还可以指定一个有参构造器,接受两个参数(SpringApplication application, String[] args)
1.3. springboot 在spring-boot.jar中配置了默认的 Listener,如下
org\springframework\boot\spring-boot\3.1.5\spring-boot-3.1.5.jar!\META-INF\spring.factories
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
/**
* Listener先要从 META-INF/spring.factories 读到
*
* 1、引导: 利用 BootstrapContext 引导整个项目启动
* starting: 应用开始,SpringApplication的run方法一调用,只要有了 BootstrapContext 就执行
* environmentPrepared: 环境准备好(把启动参数等绑定到环境变量中),但是ioc还没有创建;【调一次】
* 2、启动:
* contextPrepared: ioc容器创建并准备好,但是sources(主配置类)没加载。并关闭引导上下文;组件都没创建 【调一次】
* contextLoaded: ioc容器加载。主配置类加载进去了。但是ioc容器还没刷新(我们的bean没创建)。
* =======截止以前,ioc容器里面还没造bean呢=======
* started: ioc容器刷新了(所有bean造好了),但是 runner 没调用。
* ready: ioc容器刷新了(所有bean造好了),所有 runner 调用完了。
* 3、运行
* 以前步骤都正确执行,代表容器running。
*/
生命周期全流程
事件触发时机
各种回调监听器
BootstrapRegistryInitializer
: 感知特定阶段:感知引导初始化META-INF/spring.factories
- 创建引导上下文bootstrapContext的时候触发。
application.addBootstrapRegistryInitializer()
;- 场景:进行密钥校对授权。
ApplicationContextInitializer
: 感知特定阶段: 感知ioc容器初始化META-INF/spring.factories
application.addInitializers()
;
ApplicationListener
: 感知全阶段:基于事件机制,感知事件。 一旦到了哪个阶段可以做别的事- @Bean或@EventListener: 事件驱动
SpringApplication.addListeners(…)
或SpringApplicationBuilder.listeners(…)
META-INF/spring.factories
SpringApplicationRunListener
: 感知全阶段生命周期 + 各种阶段都能自定义操作; 功能更完善。META-INF/spring.factories
ApplicationRunner
: 感知特定阶段:感知应用就绪Ready。卡死应用,就不会就绪@Bean
CommandLineRunner
: 感知特定阶段:感知应用就绪Ready。卡死应用,就不会就绪@Bean
最佳实战:
- 如果项目启动前做事: BootstrapRegistryInitializer 和 ApplicationContextInitializer
- 如果想要在项目启动完成后做事:ApplicationRunner和 CommandLineRunner
- 如果要干涉生命周期做事:SpringApplicationRunListener
- 如果想要用事件机制:ApplicationListener
完整触发流程
9大事件触发顺序&时机
ApplicationStartingEvent
:应用启动但未做任何事情, 除过注册listeners and initializers.ApplicationEnvironmentPreparedEvent
: Environment 准备好,但context 未创建.ApplicationContextInitializedEvent
: ApplicationContext 准备好,ApplicationContextInitializers 调用,但是任何bean未加载ApplicationPreparedEvent
: 容器刷新之前,bean定义信息加载ApplicationStartedEvent
: 容器刷新完成, runner未调用
---以下就开始插入了探针机制---AvailabilityChangeEvent
:LivenessState.CORRECT
应用存活; 存活探针ApplicationReadyEvent
: 任何runner被调用AvailabilityChangeEvent
:ReadinessState.ACCEPTING_TRAFFIC
就绪探针,可以接请求ApplicationFailedEvent
:启动出错
应用事件发送顺序如下:
感知应用是否存活了:可能植物状态,虽然活着但是不能处理请求。
应用是否就绪了:能响应请求,说明确实活的比较好。
代码使用SpringApplicationRunListener
和ApplicationListener
/resource/META-INF/spring.factories
org.springframework.boot.SpringApplicationRunListener=\
com.atguigu.boot3.core.listener.MyApplicationListener
org.springframework.context.ApplicationListener=\
com.atguigu.boot3.core.listener.MyEventListener
MyApplicationListener.java
用来感知SpringBoot生命周期
package com.atguigu.boot3.core.listener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import java.time.Duration;
/**
* SpringBoot的生命周期
*/
@Slf4j
public class MyApplicationListener implements SpringApplicationRunListener {
/**
* 在run方法首次启动时立即调用。可以用于非常早期的初始化。
*/
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
System.out.println("***starting***");
}
/**
* 环境准备好就会调用,但是在 ApplicationContext 创建前
*/
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
System.out.println("***environmentPrepared***");
}
/**
* ApplicationContext已经被创建,但是还未加载
*/
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("***contextPrepared***");
}
/**
* 在ApplicationContext加载后但在刷新之前调用
*/
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("***contextLoaded***");
}
/**
* ApplicationContext已经刷新,但是CommandLineRunners和ApplicationRunners还未被调用
*/
@Override
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("***started***");
}
/**
* 在调用了CommandLineRunners和ApplicationRunners后,ApplicationContext容器run方法运行之前运行
*/
@Override
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("***ready***");
}
/**
* 在应用程序运行出错时运行
*/
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("***failed***");
}
}
MyEventListener.java
监听事件
package com.atguigu.boot3.core.listener;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
public class MyEventListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("--[event]--" + event.getClass().getName());
}
}
运行结果
E:\Java\jdk-17.0.5\bin\java.exe ...
--[event]--org.springframework.boot.context.event.ApplicationStartingEvent
***starting***
--[event]--org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
***environmentPrepared***
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.1.5)
--[event]--org.springframework.boot.context.event.ApplicationContextInitializedEvent
***contextPrepared***
2023-10-30T18:08:56.239+08:00 INFO 16448 --- [ main] com.atguigu.boot3.core.MainApplication : Starting MainApplication using Java 17.0.5 with PID 16448 (E:\code\IdeaProjects\spring-boot-3\boot3-07-core\target\classes started by 朱俊伟 in E:\code\IdeaProjects\spring-boot-3)
2023-10-30T18:08:56.242+08:00 INFO 16448 --- [ main] com.atguigu.boot3.core.MainApplication : The following 1 profile is active: "dev"
--[event]--org.springframework.boot.context.event.ApplicationPreparedEvent
***contextLoaded***
2023-10-30T18:08:57.269+08:00 INFO 16448 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-10-30T18:08:57.281+08:00 INFO 16448 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-10-30T18:08:57.281+08:00 INFO 16448 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.15]
2023-10-30T18:08:57.367+08:00 INFO 16448 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-10-30T18:08:57.368+08:00 INFO 16448 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1064 ms
2023-10-30T18:08:57.765+08:00 INFO 16448 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
--[event]--org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent
--[event]--org.springframework.context.event.ContextRefreshedEvent
2023-10-30T18:08:57.774+08:00 INFO 16448 --- [ main] com.atguigu.boot3.core.MainApplication : Started MainApplication in 2.248 seconds (process running for 2.97)
--[event]--org.springframework.boot.context.event.ApplicationStartedEvent
--[event]--org.springframework.boot.availability.AvailabilityChangeEvent
***started***
--[event]--org.springframework.boot.context.event.ApplicationReadyEvent
--[event]--org.springframework.boot.availability.AvailabilityChangeEvent
***ready***
--[event]--org.springframework.boot.availability.AvailabilityChangeEvent
--[event]--org.springframework.context.event.ContextClosedEvent
参考:
https://www.yuque.com/leifengyang/springboot3/lliphvul8b19pqxp#beI2B