【Galois工具开发之路】SpringApplicationRunListener在SpringBoot项目启动过程中执行了两次...

时间:2021-06-10 01:16:49

SpringApplicationRunListener是什么?

SpringApplicationRunListener 接口的作用主要就是在Spring Boot 启动初始化的过程中可以通过SpringApplicationRunListener接口回调来让用户在启动的各个流程中可以加入自己的逻辑。比如以下的方法

Galois通过注入各种Listener到SpringBoot的启动监听器列表中,实现了SpringBoot启动后初始化各种AgentService的功能

	default void starting(ConfigurableBootstrapContext bootstrapContext) {
		starting();
	}

	@Deprecated
	default void starting() {
	}

	default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
			ConfigurableEnvironment environment) {
		environmentPrepared(environment);
	}

	@Deprecated
	default void environmentPrepared(ConfigurableEnvironment environment) {
	}

	default void contextPrepared(ConfigurableApplicationContext context) {
	}

	default void contextLoaded(ConfigurableApplicationContext context) {
	}

	default void started(ConfigurableApplicationContext context) {
	}

	default void running(ConfigurableApplicationContext context) {
	}

	default void failed(ConfigurableApplicationContext context, Throwable exception) {
	}

问题所在

正常来说,所有listener的started 方法只会执行一次,就是在SpringBoot项目启动完成之后。但是目前遇到了执行两次的情况,在通过查看SpringBoot的源码之后,发现了 当SpringBoot作为SpringCloud的一员启动时,SpringBoot的run方法被执行了两次。下面通过源码来一步步看看。

  • 首先是进入 SpringBootApplication.run 方法来看看,里面注册了许多 ApplicationListener,其中有一个名为 BootstrapApplicationListener的ApplicationListener [org.springframework.cloud.bootstrap.BootstrapApplicationListener,在SpringCloud包下]

注意,SpringApplicationRunListener跟ApplicationListener不是同一个级别的

【Galois工具开发之路】SpringApplicationRunListener在SpringBoot项目启动过程中执行了两次...

  • 追踪代码,发现了这个BootstrapApplicationListener的实现方法,如下。最关键的地方,就是画线的这个方法,在这个方法里面,重新调用了SpringBootApplication.run方法,但不是完全调用,是变更了一些SpringBoot的配置【不启动Web容器,不打印Banner等等】,通过run方法,来构造出context上下文,并用来初始化SpringCloud的相关组件。
    【Galois工具开发之路】SpringApplicationRunListener在SpringBoot项目启动过程中执行了两次...
  • 下图为 BootstrapApplicationListener调用SpringBootApplication.run方法的关键代码,也就是这个builder.run的地方,会使得SpringBoot配置的SpringApplicationRunListener重复执行一遍。【注意给context进行setId的这个操作
    【Galois工具开发之路】SpringApplicationRunListener在SpringBoot项目启动过程中执行了两次...

问题解决

如何处理SpringApplicationRunListener被执行两遍的问题?首先,肯定是避免不了SpringBootApplication.run方法被执行两遍的命运的。虽然图中代码写明,SpringCloud的自定义执行run方法,给生成的context上下文set了id,为bootstrap,但是吧,给它设置这个id的时候,run方法已经执行完了,所以实际上开头列出的SpringApplicationRunListener接口,started方法访问到的这个context上下文,名字还是application【第二个context名为application-1】。

所以,避免started方法被执行两遍的方法,就是在started的实现方法里对入参context做一个校验,如果是id不是值为application的context,可判断为非正常SpringBootApplication.run的context对象,此时应该直接return,不执行你自己实现的started方法代码。

【Galois工具开发之路】SpringApplicationRunListener在SpringBoot项目启动过程中执行了两次...