SpringBoot核心机制三、ApplicationListener

所有调试均使用SpringBoot 2.4.5版本。

ApplicationListener事件监听机制其实是由Spring提供的,应用内部的事件驱动机制。也就是Pub/Sub发布订阅机制在应用内部的实现。一般主要是用于监控应用内部的一些运行状况,在应用开发中也可以使用。

具体的实现机制可以到Spring中去探究,这里就来简单理解下SpringBoot对这个事件驱动机制做了哪些封装。

一、事件监听器使用

1、自己实现一个事件监听器

首先创建一个自定义的事件监听器:

public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println("======>MyApplicationListener: "+applicationEvent);
    }
}

然后还是在项目的spring.factories中配置监听器

org.springframework.context.ApplicationListener=\
com.roy.applicationListener.MyApplicationListener

然后配置启动类。在启动类中发布一个自己的事件。

@SpringBootApplication
public class P1Application implements CommandLineRunner {
    public static void main(String[] args) {
        final SpringApplication application = new SpringApplication(P1Application.class);
//        application.addInitializers(new MyApplicationContextInitializer());
        application.run(args);
    }
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void run(String... args) throws Exception {
        //自行发布一个事件。
        applicationContext.publishEvent(new ApplicationEvent("selfEvent") {
        });
    }
}

正常启动SpringBoot应用,就能打印出启动过程中的关键事件的日志。这里把关键的事件日志给整理出来:

======>MyApplicationListener: org.springframework.boot.context.event.ApplicationStartingEvent[source=org.springframework.boot.SpringApplication@60c6f5b]
======>MyApplicationListener: org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent[source=org.springframework.boot.SpringApplication@60c6f5b]
======>MyApplicationListener: org.springframework.boot.context.event.ApplicationContextInitializedEvent[source=org.springframework.boot.SpringApplication@60c6f5b]
======>MyApplicationListener: org.springframework.boot.context.event.ApplicationPreparedEvent[source=org.springframework.boot.SpringApplication@60c6f5b]
======>MyApplicationListener: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@1d296da, started on Wed Apr 28 13:33:33 CST 2021]
======>MyApplicationListener: org.springframework.boot.context.event.ApplicationStartedEvent[source=org.springframework.boot.SpringApplication@60c6f5b]
======>MyApplicationListener: org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@1d296da, started on Wed Apr 28 13:33:33 CST 2021]
======>MyApplicationListener: com.roy.P1Application$1[source=selfEvent]  ##!!自行发布的事件。
======>MyApplicationListener: org.springframework.boot.context.event.ApplicationReadyEvent[source=org.springframework.boot.SpringApplication@60c6f5b]
======>MyApplicationListener: org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@1d296da, started on Wed Apr 28 13:33:33 CST 2021]
======>MyApplicationListener: org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@1d296da, started on Wed Apr 28 13:33:33 CST 2021]

这里就打印出了SpringBoot应用启动过程中的多个内部事件。实际上这多个内部事件也就对应了启动过程的各个阶段,是梳理SpringBoot启动流程非常好的入口。

Spring事件机制的其他细节这里就不多说了,大家可以自行了解。我们这里还是专注于SpringBoot的部分。

2、事件监听器的其他配置方式:

这个事件监听机制是Spring非常重要的一个机制,有非常多的配置方式。除了上面提到的基于spring.factories文件配置的方式,还有其他几种配置方式。

2.1 SpringApplication.addListener

跟之前的Initializer一样,这个事件监听器也可以在SpringApplication中直接添加。

@SpringBootApplication
public class P1Application implements CommandLineRunner {
    public static void main(String[] args) {
        final SpringApplication application = new SpringApplication(P1Application.class);
        //添加事件监听器
        application.addListeners(new MyApplicationListener());
        application.run(args);
    }
}

2.2 基于注解添加

基于注解,将MyApplicationListener配置到Spring的IOC容器中。

@Configuration
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println("======>MyApplicationListener: "+applicationEvent);
    }
}

2.3 在SpringBoot的配置文件中配置

另外还一种方式,可以在SpringBoot的配置文件application.properties中配置

context.listener.classes=com.roy.applicationListener.MyApplicationListener

这几种方式都可以配置事件监听器。另外,其实在上一章节ApplicationContextInitializer中也能看到,在SpringBoot内部也在应用初始化中扩展出了很多通过application添加事件监听器的扩展。

二、核心机制解读

事件机制的使用方式很多,不同的配置方式也有不同的加载流程。我们这里还是只解读SpringBoot中如何通过spring.factories文件来加载事件监听器的。

SpringBoot中对于监听器的处理,也跟ApplicationContextInitializer的处理流程是差不多的。首先在SpringApplication的构造方法中加载所有的监听器:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
       ...
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    	//加载spring.facotries中注册的所有事件监听器
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        ...
    }

然后在SpringApplication的run方法中启动所有监听器:

public ConfigurableApplicationContext run(String... args) {
        ...
        SpringApplicationRunListeners listeners = this.getRunListeners(args);//<====注册SpringApplicationRunListener
        listeners.starting(bootstrapContext, this.mainApplicationClass); //<===会发布ApplicationStartingEvent事件

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);//<===在这个过程中会发布ApplicationEnvironmentPreparedEvent
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// <==发布ApplicationContextInitializedEvent ApplicationPreparedEvent事件
            this.refreshContext(context);// <===发布ContextRefreshedEvent事件
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);//<===发布ApplicationStartedEvent AvailabilityChangeEvent事件
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);//发布ApplicationReadyEvent 和 AvailabilityChangeEvent事件
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

首先在注册SpringApplicationRunListener时,就会解析spring.factories,读取其中的org.springframework.boot.SpringApplicationRunListener配置。

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

然后从发布事件的地方往下调试,可以看到SpringBoot事件发布的核心对象EventPublishingRunListener。通过其中的initialMulticaster组件来发布不同的事件。 而他实现事件监听的方式就是在发布事件时,实时调用一下已经注册的所有对应事件的监听器。

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public void starting(ConfigurableBootstrapContext bootstrapContext) {
		//发布事件
        this.initialMulticaster
				.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args)); 
	}
    .....
}

调用Spring中的SimpleApplicationEventMulticaster组件发布事件。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    ...

    public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        Executor executor = this.getTaskExecutor();
        //根据event查找注册的监听器
        Iterator var5 = this.getApplicationListeners(event, type).iterator();

        while(var5.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var5.next();
            if (executor != null) {
                executor.execute(() -> {
                    //调用Listener的onApplicationEvent方法。
                    this.invokeListener(listener, event);
                });
            } else {
                this.invokeListener(listener, event);
            }
        }

    }

这其中initialMulticaster对象已经是Spring-Context包中的内容。所以这里就完成了SpringBoot中事务监听机制的梳理。

这个事件机制也是SpringBoot使用过程中非常好的功能扩展点,因为应用启动过程中,这些事件都已经默认发布了,可以叠加自己想要的应用初始化工作。这些关键事件的发布顺序也是非常重要的。例如,如果你的扩展功能需要用到Spring的IOC容器,那就只能去监听ContextRefreshedEvent之后的几个内部事件。

三、SpringBoot中的核心实现

接下来梳理SpringBoot当中通过spring.factories默认注册的事务监听器

#spirng-boot.jar
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
#spring-boot-autoconfigure
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

那接下来同样是梳理几个有代表性的事务监听器逻辑。

例如BackgroundPreinitializer,他会将几个比较耗时的初始化工作提前到应用启动过程中加载,并且单独启动一个线程来加快加载速度。

@Order(LoggingApplicationListener.DEFAULT_ORDER + 1)
public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent> {

    //这段说明很重要
	/**
	 * System property that instructs Spring Boot how to run pre initialization. When the
	 * property is set to {@code true}, no pre-initialization happens and each item is
	 * initialized in the foreground as it needs to. When the property is {@code false}
	 * (default), pre initialization runs in a separate thread in the background.
	 * @since 2.1.0
	 */
	public static final String IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME = "spring.backgroundpreinitializer.ignore";

	private static final AtomicBoolean preinitializationStarted = new AtomicBoolean();

	private static final CountDownLatch preinitializationComplete = new CountDownLatch(1);

	private static final boolean ENABLED;

	static {
		ENABLED = !Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME) && !NativeDetector.inNativeImage()
				&& Runtime.getRuntime().availableProcessors() > 1;
	}
	//监听SpringApplicationEvent事件。这个事件是之前调试过的几个重要事件的父接口
	@Override
	public void onApplicationEvent(SpringApplicationEvent event) {
		if (!ENABLED) {
			return;
		}
		if (event instanceof ApplicationEnvironmentPreparedEvent
				&& preinitializationStarted.compareAndSet(false, true)) {
			performPreinitialization();
		}
		if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
				&& preinitializationStarted.get()) {
			try {
				preinitializationComplete.await();
			}
			catch (InterruptedException ex) {
				Thread.currentThread().interrupt();
			}
		}
	}
	...
}

接下来其他几个事件监听器的功能也简单总结下。有兴趣的建议自行调试下代码,这样才能形成自己的理解。

  • org.sf.boot.ClearCachesApplicationListener:应用上下文加载完成后对缓存做清除工作,响应事件ContextRefreshedEvent

  • org.sf.boot.builder.ParentContextCloserApplicationListener:监听双亲应用上下文的关闭事件并往自己的孩子应用上下文中传播,相关事件ParentContextAvailableEvent/ContextClosedEvent

  • org.sf.boot.context.FileEncodingApplicationListener:如果系统文件编码和环境变量中指定的不同则终止应用启动。
    具体的方法是比较系统属性file.encoding和环境变量spring.mandatory-file-encoding是否相等(大小写不敏感)。

  • org.sf.boot.context.config.AnsiOutputApplicationListener:根据spring.output.ansi.enabled参数配置AnsiOutput

  • org.sf.boot.context.config.DelegatingApplicationListener:监听到事件后转发给环境变量context.listener.classes指定的那些事件监听器

  • org.sf.boot.context.logging.LoggingApplicationListener 配置LoggingSystem。使用logging.config环境变量指定的配置或者缺省配置

  • org.springframework.boot.env.EnvironmentPostProcessorApplicationListener: 加载spring.factories文件中配置的EnvironmentPostProcessor。

  • org.sf.boot.liquibase.LiquibaseServiceLocatorApplicationListener 使用一个可以和Spring Boot可执行jar包配合工作的版本替换liquibase ServiceLocator

org.sf.boot.context.config.ConfigFileApplicationListener : 指定SpringBoot的配置文件地址。 但是在2.4.5版本已经移除。

最后,有没有觉得EnvironmentPostProcessorApplicationListener这个事件监听器挺有意思的?SpringBoot直接将spring.factories中EnvironmentPostProcessor机制的加载工作交给了事件监听器,接下来我们就趁热打铁,来继续看下EnvironmentPostProcessor 的处理机制。

四、EnvironmentPostProcessor使用

EnvironmentPostProcessor是在环境信息加载完成后进行一些补充处理。例如下面一个示例可以在SpringBoot读取完application.properties后,补充读取另一个配置文件:

//@Component
public class MyEnvironmentPostProcessor  implements EnvironmentPostProcessor{
 
	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
		try(InputStream input = new FileInputStream("E:\\ds.properties")) {
			Properties properties = new Properties();
			properties.load(input);
			PropertiesPropertySource propertySource = new PropertiesPropertySource("ve", properties);
			environment.getPropertySources().addLast(propertySource);
			System.out.println("====加载外部配置文件完毕====");
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}

使用的方式同样可以配置到spring.factories文件中,或者通过@Component注解加入到IOC容器中。

org.springframework.boot.env.EnvironmentPostProcessor=\
com.roy.environmentPostProcessor.MyEnvironmentPostProcessor

四、EnvironmentPostProcessor 加载机制

EnvironmentPostProcessor 是通过EnvironmentPostProcessorApplicationListener监听Spring事件来对运行环境进行补充的后续处理。先来看下他监听了哪些事件。

public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {

    //执行优先级非常高
	/**
	 * The default order for the processor.
	 */
	public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
	...
	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent((ApplicationPreparedEvent) event);
		}
		if (event instanceof ApplicationFailedEvent) {
			onApplicationFailedEvent((ApplicationFailedEvent) event);
		}
	}

从这几个事件当中可以看出,在他的子处理器中是不能引用IOC容器的

然后再往下继续调试他的onApplicationEnvironmentPreparedEvent方法,找下他解析spring.factories,加载EnvironmentPostProcessor 实现的机制。这个机制就不再是使用 SpringFactoriesLoader 了。

//处理EnvironmentPostProcessor的方式。
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
		ConfigurableEnvironment environment = event.getEnvironment();
		SpringApplication application = event.getSpringApplication();
    	//重点跟踪getEnvironmentPostProcessors方法
		for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(event.getBootstrapContext())) {
			postProcessor.postProcessEnvironment(environment, application);
		}
	}

一路往下调试,会跟踪到ReflectionEnvironmentPostProcessorsFactory的getEnvironmentPostProcessors方法。

public List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory,
			ConfigurableBootstrapContext bootstrapContext) {
		Instantiator<EnvironmentPostProcessor> instantiator = new Instantiator<>(EnvironmentPostProcessor.class,
				(parameters) -> {
					parameters.add(DeferredLogFactory.class, logFactory);
					parameters.add(Log.class, logFactory::getLog);
					parameters.add(ConfigurableBootstrapContext.class, bootstrapContext);
					parameters.add(BootstrapContext.class, bootstrapContext);
					parameters.add(BootstrapRegistry.class, bootstrapContext);
				});
		return instantiator.instantiate(this.classNames);
	}

这个parameters就是给这一组实例添加一些默认的参数类。后面各个子类可以创建包含了这些参数的构造方法来进行实例化。 有没有发现,这就是一个简单的IOC属性注入吗?学IOC有比这个更经典的入手案例吗?

具体实例化的方式在Instantiator这个类中

private T instantiate(Class<?> type) throws Exception {
		Constructor<?>[] constructors = type.getDeclaredConstructors();
		Arrays.sort(constructors, CONSTRUCTOR_COMPARATOR);
		for (Constructor<?> constructor : constructors) {
            //args就来自于上面指定的几个类
			Object[] args = getArgs(constructor.getParameterTypes());
			if (args != null) {
				ReflectionUtils.makeAccessible(constructor);
				return (T) constructor.newInstance(args);
			}
		}
		throw new IllegalAccessException("Unable to find suitable constructor");
	}

五、SpringBoot中注册的EnvironmentPostProcessor实现

接下来还是梳理下SpringBoot在spring.factories当中注册的EnvironmentPostProcessor实现:

#spirng-boot.jar当中
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor,\
org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor

这些实现类具体是干什么的,这个我暂时还看不太懂,应该跟具体的一些细节场景有关了。网上也暂时没有找到相关的资料。就不再去具体分析了。

不得不吐槽下,网上的帖子千篇一律只讲了怎么用EnvironmentPostProcesser读取额外的配置文件,但是这些机制还真没有人来分析过。

接下来简单跟踪了一下ConfigDataEnvironmentPostProcessor这个实现。发现他的实现过程中又引入了spring.factories中的org.springframework.boot.context.config.ConfigDataLoader功能机制。又是一个庞大复杂的功能机制,目前我也没太弄明白他的具体功能。以后了解到了再补上把。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
您可以通过实现 `ApplicationListener` 接口来添加 `SpringBoot` 启动时的监听器。以下是一个示例: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; @SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(MyApplication.class); app.addListeners(new MyApplicationListener()); // 添加自定义的监听器 app.run(args); } // 自定义的监听器 public static class MyApplicationListener implements ApplicationListener<ApplicationEvent> { @Override public void onApplicationEvent(ApplicationEvent event) { // 在应用程序启动时触发的逻辑处理 System.out.println("应用程序启动了!"); } } } ``` 在上面的示例中,我们创建了一个 `MyApplicationListener` 类,实现了 `ApplicationListener` 接口,并重写了其 `onApplicationEvent` 方法,用于在应用程序启动时执行自定义逻辑。 在 `main` 方法中,我们创建了一个 `SpringApplication` 对象,并通过 `addListeners` 方法将自定义的监听器添加到应用程序中,然后调用 `run` 方法启动应用程序。 当应用程序启动时,`onApplicationEvent` 方法将被调用,并执行我们定义的逻辑处理。这里只是简单地打印一条消息,您可以根据实际需求进行相应的处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

roykingw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值