2018 年 7 月 3 日
Springboot 启动过程中,使用了 SpringApplicationRunListener 监听器来监听启动过程的运行情况。那么它是如何工作的呢?
Spring 在这里设计得很精巧。使用的是观察者模式,可以无需对启动时的其它业务 bean 的配置关心,只需要正常启动创建 Spring 应用上下文环境。。将 SpringApplicationRunListeners
作为信息发布者 ,初始化过程中通过 META_INF/spring.factories
文件中指定的监听器实例化出来的各个 SpringApplicationRunListener
作为信息订阅者。
各个业务'监听观察者'在监听到 spring 开始启动,或环境准备完成等事件后,会按照自己的逻辑创建所需的 bean 或者进行相应的配置。观察者模式使 run 方法的结构变得清晰,同时与外部耦合降到最低。
SpringApplicationEvent 事件
在启动 Spring Boot 时所发生的事件一般指:
- 开始启动事件(ApplicationStartedEvent)
- 环境准备完成事件(ApplicationEnvironmentPreparedEvent)
- 上下文准备完成事件(无)
- 上下文加载完成(ApplicationPreparedEvent)
- 应用启动完成事件(ApplicationReadyEvent 或者 ApplicationFailedEvent)
Spring 启动时的每个事件都是继承自SpringApplicationEvent
抽象类的一个实现,每一个事件都包含了应用的SpringApplication
对象和应用程序启动时的参数。
SpringApplicationRunListener 监听器
/**
* Listener for the {@link SpringApplication} {@code run} method.
* {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}
* and should declare a public constructor that accepts a {@link SpringApplication}
* instance and a {@code String[]} of arguments. A new
* {@link SpringApplicationRunListener} instance will be created for each run.
*
* @author Phillip Webb
* @author Dave Syer
*/
public interface SpringApplicationRunListener {
/**
* 在run方法业务逻辑执行、应用上下文初始化前调用此方法
*/
void starting();
/**
* 当环境准备完成,应用上下文被创建之前调用此方法
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* 在应用上下文被创建和准备完成之后,但上下文相关代码被加载执行之前调用。因为上下文准备事件和上下文加载事件难以明确区分,所以这个方法一般没有具体实现。
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* 当上下文加载完成之后,自定义bean完全加载完成之前调用此方法。
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* 当run方法执行完成,或执行过程中发现异常时调用此方法。
*/
void finished(ConfigurableApplicationContext context, Throwable exception);
}
Spring Boot 在启动过程中会实例化 EventPublishingRunListener
作为 SpringApplicationRunListener
的实例。在实例化监听器时需要 SpringApplication
对象和用户对象作为参数。其内部维护着一个事件广播器(被观察者对象集合,前面所提到的在 META_INF/spring.factories
中注册的初始化监听器的有序集合 ),当监听到 Spring 启动等事件发生后,就会将创建具体事件对象,并广播推送给各个被观察者。
###SimpleApplicationEventMulticaster 事件广播
EventPublishingRunListener
中采用 SimpleApplicationEventMulticaster
作为事件广播器。SimpleApplicationEventMulticaster
是 ApplicationEventMulticaster
的一个实现。
EventPublishingRunListener
初始化时,将作为参数传入的 SpringApplication
中的所有 listener 放入广播器中,供各个方法使用。广播代码如下:
//将事件广播出去
@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}
/**
* 把指定事件交给指定监听器.
*/
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || msg.startsWith(event.getClass().getName())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
最终调用 ApplicationListener 的 onApplicationEvent 方法对事件做出处理。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于