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());
        }
    }
}