Springboot3生命周期监听的使用和源码解析
目录
springboot3生命周期监听的使用和源码解析
有些时候我们需要监听springboot运用的各个生命周期,常见的有启动成功,和运用启动失败。
1. MySpringApplicationRunListener
定义一个类MySpringApplicationRunListener
实现:
@Slf4j
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
SpringApplicationRunListener.super.starting(bootstrapContext);
log.info("cn.com.accumulate.admin.listener.MySpringApplicationRunListener.starting");
}
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
SpringApplicationRunListener.super.environmentPrepared(bootstrapContext, environment);
log.info("cn.com.accumulate.admin.listener.MySpringApplicationRunListener.environmentPrepared");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
SpringApplicationRunListener.super.contextPrepared(context);
log.info("cn.com.accumulate.admin.listener.MySpringApplicationRunListener.contextPrepared");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
SpringApplicationRunListener.super.contextLoaded(context);
log.info("cn.com.accumulate.admin.listener.MySpringApplicationRunListener.contextLoaded");
}
@Override
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
SpringApplicationRunListener.super.started(context, timeTaken);
log.info("cn.com.accumulate.admin.listener.MySpringApplicationRunListener.started");
}
@Override
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
SpringApplicationRunListener.super.ready(context, timeTaken);
log.info("cn.com.accumulate.admin.listener.MySpringApplicationRunListener.ready");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
SpringApplicationRunListener.super.failed(context, exception);
log.info("cn.com.accumulate.admin.listener.MySpringApplicationRunListener.failed");
}
}
spring.factories
在resources
目录下创建META-INF/spring.factories
org.springframework.boot.SpringApplicationRunListener=\
cn.com.accumulate.admin.listener.MySpringApplicationRunListener
启动日志
c.c.a.a.l.MySpringApplicationRunListener environmentPrepared 28 : cn.com.accumulate.admin.listener.MySpringApplicationRunListener.environmentPrepared
c.c.a.a.l.MySpringApplicationRunListener contextPrepared 34 : cn.com.accumulate.admin.listener.MySpringApplicationRunListener.contextPrepared
c.c.a.a.l.MySpringApplicationRunListener contextLoaded 40 : cn.com.accumulate.admin.listener.MySpringApplicationRunListener.contextLoaded
c.c.a.a.l.MySpringApplicationRunListener started 46 : cn.com.accumulate.admin.listener.MySpringApplicationRunListener.started
c.c.a.a.l.MySpringApplicationRunListener ready 52 : cn.com.accumulate.admin.listener.MySpringApplicationRunListener.ready
源码:
org.springframework.boot.SpringApplication#run(java.lang.String...)
public ConfigurableApplicationContext run(String... args) {
Startup startup = Startup.create();
if (this.registerShutdownHook) {
SpringApplication.shutdownHook.enableShutdownHookAddition();
}
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
startup.started();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);
}
listeners.started(context, startup.timeTakenToStarted());
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
if (context.isRunning()) {
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
事件触发时机
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。卡死应用,就不会就绪(用于感知Runner的运行情况)
- @Bean
CommandLineRunner
- 感知特定阶段:感知应用就绪Ready。卡死应用,就不会就绪(用于感知Runner的运行情况)
- @Bean
- 如果项目启动前做事: BootstrapRegistryInitializer 和 ApplicationContextInitializer
- 如果想要在项目启动完成后做事:ApplicationRunner和 CommandLineRunner
- 如果要干涉生命周期做事:SpringApplicationRunListener
- 如果想要用事件机制:ApplicationListener
场景运用
场景:
有一个独立的springboot工程,他可以根据传入的模型路径加载不同的模型服务并对外提供rest服务,入参和接口基础路径都是固定的,ip+端口是服务发布的时候指定的,出参根据模型不同返回不同的json串;
由于发布服务和真是提供rest服务的springboot是两个工程,所以我们需要在独立springboot工程启动成功/失败的时候调用一个回调地址来同步服务的状态
@Slf4j
public class MyApplicationRunListener implements SpringApplicationRunListener {
private final SpringApplication application;
private final String[] args;
private final String SYNC_SERVICE_STATUS_API = "/labServiceManage/syncServiceStatus";
private ConfigurableEnvironment environment;
public MyApplicationRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
}
@Override
public void started(ConfigurableApplicationContext context) {
log.debug("=====success=====应用启动成功======");
this.syncServiceStatus(2);
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.environment = environment;
log.debug("=====environmentPrepared=====环境准备完成======");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
log.debug("=====failed=====应用启动失败======");
this.syncServiceStatus(4);
}
// 示例:获取特定属性值
private String getProperty(String propertyName) {
if (environment != null) {
return environment.getProperty(propertyName);
}
return null;
}
private void syncServiceStatus(Integer status){
String applicationName = this.getProperty("spring.application.name");
if(StringUtils.isBlank(applicationName)){
return;
}
String baseUrl = this.getProperty("ailaboratory.base.url");
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("applicationName", applicationName);
paramMap.put("serviceStatus", status);
log.debug("请求入参:{}, 请求基础路径:{}", JSON.toJSONString(paramMap), baseUrl);
try(HttpResponse response = HttpUtil.createPost(baseUrl + SYNC_SERVICE_STATUS_API)
.setConnectionTimeout(60 * 1000)
.contentType("application/json")
.body(JSON.toJSONString(paramMap)).execute()){
log.debug("同步状态结果:{}", response.body());
}
}
}