9 SpringBoot事件监听机制

1 温故而知新

温故而知新,我们来简单回顾一下上篇的内容,上一篇我们分析了SpringApplication 对象的构建过程及 SpringBoot 自己实现的一套 SPI 机制,现将关键步骤再浓缩总结下:

  1. SpringApplication对象的构造过程其实就是给SpringApplication类的6个成员变量赋值;
  2. SpringBoot 通过以下步骤实现自己的 SPI 机制:
  • 1)首先获取线程上下文类加载器;
  • 2)然后利用上下文类加载器从spring.factories配置文件中加载所有的 SPI 扩展实现类并放入缓存中;
  • 3)根据 SPI 接口从缓存中取出相应的 SPI 扩展实现类;
  • 4)实例化从缓存中取出的 SPI 扩展实现类并返回。

2 引言

在 SpringBoot 启动过程中,每个不同的启动阶段会分别广播不同的内置生命周期事件,然后相应的监听器会监听这些事件来执行一些初始化逻辑工作比如ConfigFileApplicationListener会监听onApplicationEnvironmentPreparedEvent事件来加载配置文件application.properties的环境变量等。

因此本篇内容将来分析下 SpringBoot 的事件监听机制的源码。

3 SpringBoot 广播内置生命周期事件流程分析

为了探究 SpringBoot 广播内置生命周期事件流程,我们再来回顾一下 SpringBoot 的启动流程代码:

// SpringApplication.java

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	// 【0】新建一个SpringApplicationRunListeners对象用于发射SpringBoot启动过程中的生命周期事件
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 【1】》》》》》发射【ApplicationStartingEvent】事件,标志SpringApplication开始启动
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
				args);
		// 【2】》》》》》发射【ApplicationEnvironmentPreparedEvent】事件,此时会去加载application.properties等配置文件的环境变量,同时也有标志环境变量已经准备好的意思
		ConfigurableEnvironment environment = prepareEnvironment(listeners,
				applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(
				SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		// 【3】》》》》》发射【ApplicationContextInitializedEvent】事件,标志context容器被创建且已准备好
		// 【4】》》》》》发射【ApplicationPreparedEvent】事件,标志Context容器已经准备完成
		prepareContext(context, environment, listeners, applicationArguments,
				printedBanner);
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
					.logStarted(getApplicationLog(), stopWatch);
		}
		// 【5】》》》》》发射【ApplicationStartedEvent】事件,标志spring容器已经刷新,此时所有的bean实例都已经加载完毕
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
	// 【6】》》》》》发射【ApplicationFailedEvent】事件,标志SpringBoot启动失败
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}
	try {
		// 【7】》》》》》发射【ApplicationReadyEvent】事件,标志SpringApplication已经正在运行即已经成功启动,可以接收服务请求了。
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

可以看到 SpringBoot 在启动过程中首先会先新建一个SpringApplicationRunListeners对象用于发射 SpringBoot 启动过程中的各种生命周期事件,比如发射ApplicationStartingEvent,ApplicationEnvironmentPreparedEventApplicationContextInitializedEvent等事件,然后相应的监听器会执行一些 SpringBoot 启动过程中的初始化逻辑。那么,监听这些 SpringBoot 的生命周期事件的监听器们是何时被加载实例化的呢?还记得上篇文章在分析SpringApplication的构建过程吗?没错,这些执行初始化逻辑的监听器们正是在SpringApplication的构建过程中根据ApplicationListener接口去spring.factories配置文件中加载并实例化的。

3.1 为广播 SpringBoot 内置生命周期事件做前期准备

3.1.1 加载 ApplicationListener 监听器实现类

我们再来回顾下SpringApplication 对象是如何构建的? SpringBoot 源码(八)一文中讲到在构建SpringApplication对象时的setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));这句代码。

这句代码做的事情就是从spring.factories中加载出ApplicationListener事件监听接口的 SPI 扩展实现类然后添加到SpringApplication对象的listeners集合中,用于后续监听 SpringBoot 启动过程中的事件,来执行一些初始化逻辑工作。

SpringBoot 启动时的具体监听器们都实现了ApplicationListener接口,其在spring.factories部分配置如下:

不过在调试时,会从所有的 spring.factories 配置文件中加载监听器,最终加载了 10 个监听器。如下图:

3.1.2 加载 SPI 扩展类 EventPublishingRunListener

前面讲到,在 SpringBoot 的启动过程中首先会先新建一个SpringApplicationRunListeners对象用于发射 SpringBoot 启动过程中的生命周期事件,即我们现在来看下SpringApplicationRunListeners listeners = getRunListeners(args);这句代码:

// SpringApplication.java

private SpringApplicationRunListeners getRunListeners(String[] args) {
	// 构造一个由SpringApplication.class和String[].class组成的types
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
	// 1) 根据SpringApplicationRunListener接口去spring.factories配置文件中加载其SPI扩展实现类
	// 2) 构建一个SpringApplicationRunListeners对象并返回
	return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
			SpringApplicationRunListener.class, types, this, args));
}

我们将重点放到getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)这句代码,getSpringFactoriesInstances这个方法我们已经很熟悉,在上一篇分析 SpringBoot 的 SPI 机制时已经详细分析过这个方法。可以看到 SpringBoot 此时又是根据SpringApplicationRunListener这个 SPI 接口去spring.factories中加载相应的 SPI 扩展实现类,我们直接去spring.factories中看看SpringApplicationRunListener有哪些 SPI 实现类:

由上图可以看到,SpringApplicationRunListener只有EventPublishingRunListener这个 SPI 实现类
EventPublishingRunListener这个哥们在 SpringBoot 的启动过程中尤其重要,由其在 SpringBoot 启动过程的不同阶段发射不同的 SpringBoot 的生命周期事件,**SpringApplicationRunListeners**对象没有承担广播事件的职责,而最终是委托**EventPublishingRunListener**这个哥们来广播事件的。

因为从spring.factories中加载EventPublishingRunListener类后还会实例化该类,那么我们再跟进EventPublishingRunListener的源码,看看其是如何承担发射 SpringBoot 生命周期事件这一职责的?

// EventPublishingRunListener.java

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;
	/**
	 * 拥有一个SimpleApplicationEventMulticaster事件广播器来广播事件
	 */
	private final SimpleApplicationEventMulticaster initialMulticaster;

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		// 新建一个事件广播器SimpleApplicationEventMulticaster对象
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		// 遍历在构造SpringApplication对象时从spring.factories配置文件中获取的事件监听器
		for (ApplicationListener<?> listener : application.getListeners()) {
			// 将从spring.factories配置文件中获取的事件监听器们添加到事件广播器initialMulticaster对象的相关集合中
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

	@Override
	public int getOrder() {
		return 0;
	}
	// 》》》》》发射【ApplicationStartingEvent】事件
	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(
				new ApplicationStartingEvent(this.application, this.args));
	}
	// 》》》》》发射【ApplicationEnvironmentPreparedEvent】事件
	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}
	// 》》》》》发射【ApplicationContextInitializedEvent】事件
	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(
				this.application, this.args, context));
	}
	// 》》》》》发射【ApplicationPreparedEvent】事件
	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		this.initialMulticaster.multicastEvent(
				new ApplicationPreparedEvent(this.application, this.args, context));
	}
	// 》》》》》发射【ApplicationStartedEvent】事件
	@Override
	public void started(ConfigurableApplicationContext context) {
		context.publishEvent(
				new ApplicationStartedEvent(this.application, this.args, context));
	}
	// 》》》》》发射【ApplicationReadyEvent】事件
	@Override
	public void running(ConfigurableApplicationContext context) {
		context.publishEvent(
				new ApplicationReadyEvent(this.application, this.args, context));
	}
	// 》》》》》发射【ApplicationFailedEvent】事件
	@Override
	public void failed(ConfigurableApplicationContext context, Throwable exception) {
		ApplicationFailedEvent event = new ApplicationFailedEvent(this.application,
				this.args, context, exception);
		if (context != null && context.isActive()) {
			// Listeners have been registered to the application context so we should
			// use it at this point if we can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all of the context's listeners instead
			if (context instanceof AbstractApplicationContext) {
				for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
						.getApplicationListeners()) {
					this.initialMulticaster.addApplicationListener(listener);
				}
			}
			this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
			this.initialMulticaster.multicastEvent(event);
		}
	}

	// ...省略非关键代码
}

可以看到EventPublishingRunListener类实现了SpringApplicationRunListener接口,SpringApplicationRunListener接口定义了 SpringBoot 启动时发射生命周期事件的接口方法,而EventPublishingRunListener类正是通过实现SpringApplicationRunListener接口的starting,environmentPreparedcontextPrepared等方法来广播 SpringBoot 不同的生命周期事件,我们直接看下SpringApplicationRunListener接口源码好了:

// SpringApplicationRunListener.java

public interface SpringApplicationRunListener {

	void starting();

	void environmentPrepared(ConfigurableEnvironment environment);

	void contextPrepared(ConfigurableApplicationContext context);

	void contextLoaded(ConfigurableApplicationContext context);

	void started(ConfigurableApplicationContext context);

	void running(ConfigurableApplicationContext context);

	void failed(ConfigurableApplicationContext context, Throwable exception);
}

我们再接着分析EventPublishingRunListener这个类,可以看到其有一个重要的成员属性initialMulticaster,该成员属性是SimpleApplicationEventMulticaster类对象,该类正是承担了广播 SpringBoot 启动时生命周期事件的职责,**EventPublishingRunListener**对象没有承担广播事件的职责,而最终是委托**SimpleApplicationEventMulticaster**这个哥们来广播事件的。EventPublishingRunListener的源码中也可以看到在starting,environmentPreparedcontextPrepared等方法中也正是通过调用SimpleApplicationEventMulticaster类对象的multicastEvent方法来广播事件的。

思考 SpringBoot 启动过程中发射事件时事件广播者是层层委托职责的,起初由SpringApplicationRunListeners对象承担,然后SpringApplicationRunListeners对象将广播事件职责委托给EventPublishingRunListener对象,最终EventPublishingRunListener对象将广播事件的职责委托给SimpleApplicationEventMulticaster对象。为什么要层层委托这么做呢? 这个值得大家思考。

前面讲到从spring.factories中加载出EventPublishingRunListener类后会实例化,而实例化必然会通过EventPublishingRunListener的构造函数来进行实例化,因此我们接下来分析下EventPublishingRunListener的构造函数源码:

// EventPublishingRunListener.java

public EventPublishingRunListener(SpringApplication application, String[] args) {
	this.application = application;
	this.args = args;
	// 新建一个事件广播器SimpleApplicationEventMulticaster对象
	this.initialMulticaster = new SimpleApplicationEventMulticaster();
	// 遍历在构造SpringApplication对象时从spring.factories配置文件中获取的事件监听器
	for (ApplicationListener<?> listener : application.getListeners()) {
		// 将从spring.factories配置文件中获取的事件监听器们添加到事件广播器initialMulticaster对象的相关集合中
		this.initialMulticaster.addApplicationListener(listener);
	}
}

可以看到在EventPublishingRunListener的构造函数中有一个for循环会遍历之前从spring.factories中加载的监听器们,然后添加到集合中缓存起来,用于以后广播各种事件时直接从这个集合中取出来即可,而不用再去spring.factories中加载,提高效率。

3.2 广播 SpringBoot 的内置生命周期事件

spring.factories配置文件中加载并实例化EventPublishingRunListener对象后,那么在在 SpringBoot 的启动过程中会发射一系列 SpringBoot 内置的生命周期事件,我们再来回顾下 SpringBoot 启动过程中的源码:

// SpringApplication.java

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	// 【0】新建一个SpringApplicationRunListeners对象用于发射SpringBoot启动过程中的生命周期事件
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 【1】》》》》》发射【ApplicationStartingEvent】事件,标志SpringApplication开始启动
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
				args);
		// 【2】》》》》》发射【ApplicationEnvironmentPreparedEvent】事件,此时会去加载application.properties等配置文件的环境变量,同时也有标志环境变量已经准备好的意思
		ConfigurableEnvironment environment = prepareEnvironment(listeners,
				applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(
				SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		// 【3】》》》》》发射【ApplicationContextInitializedEvent】事件,标志context容器被创建且已准备好
		// 【4】》》》》》发射【ApplicationPreparedEvent】事件,标志Context容器已经准备完成
		prepareContext(context, environment, listeners, applicationArguments,
				printedBanner);
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
					.logStarted(getApplicationLog(), stopWatch);
		}
		// 【5】》》》》》发射【ApplicationStartedEvent】事件,标志spring容器已经刷新,此时所有的bean实例都已经加载完毕
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
	// 【6】》》》》》发射【ApplicationFailedEvent】事件,标志SpringBoot启动失败
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}
	try {
		// 【7】》》》》》发射【ApplicationReadyEvent】事件,标志SpringApplication已经正在运行即已经成功启动,可以接收服务请求了。
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

可以看到在 SpringBoot 的启动过程中总共会发射 7 种不同类型的生命周期事件,来标志 SpringBoot 的不同启动阶段,同时,这些生命周期事件的监听器们也会执行一些启动过程中的初始化逻辑,关于这些监听器的初始化逻辑将在下一篇内容中会分析。以下是 SpringBoot 启动过程中要发射的事件类型,其中ApplicationFailedEvent在 SpringBoot 启动过程中遇到异常才会发射:

  1. ApplicationStartingEvent
  2. ApplicationEnvironmentPreparedEvent
  3. ApplicationContextInitializedEvent
  4. ApplicationPreparedEvent
  5. ApplicationStartedEvent
  6. ApplicationFailedEvent
  7. ApplicationReadyEvent

我们以listeners.starting();这句代码为例,看看EventPublishingRunListener对象发射事件的源码:

// SpringApplicationRunListeners.java

public void starting() {
	// 遍历listeners集合,这里实质取出的就是刚才从spring.factories中取出的SPI实现类EventPublishingRunListener
	// 而EventPublishingRunListener对象承担了SpringBoot启动过程中负责广播不同的生命周期事件
	for (SpringApplicationRunListener listener : this.listeners) {
	        // 调用EventPublishingRunListener的starting方法来广播ApplicationStartingEvent事件
		listener.starting();
	}
}

继续跟进listener.starting();的源码:

EventPublishingRunListener.java

// 》》》》》发射【ApplicationStartingEvent】事件
public void starting() {
	// EventPublishingRunListener对象将发布ApplicationStartingEvent这件事情委托给了initialMulticaster对象
	// 调用initialMulticaster的multicastEvent方法来发射ApplicationStartingEvent事件
	this.initialMulticaster.multicastEvent(
			new ApplicationStartingEvent(this.application, this.args));
}

可以看到,EventPublishingRunListener对象将发布ApplicationStartingEvent这件事情委托给了SimpleApplicationEventMulticaster对象initialMulticaster,
,而initialMulticaster对象最终会调用其multicastEvent方法来发射ApplicationStartingEvent事件。关于SimpleApplicationEventMulticaster类如何广播事件,笔者已经在Spring 是如何实现事件监听机制的? Spring 源码(二)这篇文章已经详细分析,这里不再赘述。

关于 SpringBoot 启动过程中发射其他生命周期事件的源码这里不再分析

4 SpringBoot 的内置生命周期事件总结

好了,前面已经分析了 SpringBoot 启动过程中要发射的各种生命周期事件,下面列一个表格总结下:

5 小结

SpringBoot 启动过程中广播生命周期事件的源码分析就到此结束了,下一篇会继续介绍监听这些生命周期事件的监听器们。我们再回顾本篇内容总结下关键点:

SpringBoot 启动过程中会发射 7 种类型的生命周期事件,标志不同的启动阶段,然后相应的监听器会监听这些事件来执行一些初始化逻辑工作。

【源码笔记】Github 源码分析项目上线啦!!!下面是笔记的 Github 地址:

https://github.com/yuanmabiji/Java-SourceCode-Blogs

原创不易,帮忙 Star 一下呗

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值