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

/images/springboot/springboot3-1-1.png

/images/springboot/springboot3-1-2.png

事件触发时机

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