提示: 此源码解析基于Spring Boot 2.1.1以及Spring Framework 5.1.3版本。
由于笔者做Web开发居多,因此在详细解析之前先上个AnnotationConfigServletWebServerApplicationContext
的类图吧。它是ApplicationContext
在web环境下的默认实现。
在Spring Boot官方文本档中关于events和listeners中可以看到这么一段描述。
Some events are actually triggered before the ApplicationContext is created, so you cannot register a listener on those as a @Bean. You can register them with the SpringApplication.addListeners(…) method or the SpringApplicationBuilder.listeners(…) method.
这段解释很直白,我这边就不做翻译了,结合代码之后得出一个结论:Spring Boot事件的分发其实分两个阶段。
ApplicationContext
尚未完成refresh时,Spring Boot使用EventPublishingRunListener
进行事件分发。SpringApplication
的run(String... args)
中,可以看到这么一行,public ConfigurableApplicationContext run(String... args) {
xxx
//创建分发器 提供启动过程中各种事件的分发
SpringApplicationRunListeners listeners = getRunListeners(args);
xxx
}
具体实现如下:
/**
* 创建run()方法 监听器
* 通过SrpingFactory获取实例 进行初始化完成前事件推送
* 默认实现为{@link org.springframework.boot.context.event.EventPublishingRunListener}
* called by {@link #run(String...)}
* @param args
* @return
*/
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
而SpringApplicationRunListener
的唯一实现正是EventPublishingRunListener
,来看下它的注释。
/**
* 此Class用于在context被完全初始化之前传递消息。
* 在初始化完成之前,Srping默认的消息分发器{@link SpringApplicationRunListener}并不可用。
* 此分发器传递的任何消息不会被用户自定义的监听器监听到。
* {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s.
*
* Uses an internal {@link ApplicationEventMulticaster} for the events that are fired
* before the context is actually refreshed.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @author Andy Wilkinson
* @author Artsiom Yudovin
*/
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered
其实也很容易理解,在这个阶段,Spring Boot尚未初始化Spring FrameWork的组件,自然没有办法使用Spring FrameWork的事件分发机制,因此只能自己实现事件分发来进行启动阶段的代码解耦。
SpringApplication
会调用refresh
这个方法。可以看的出来,其实调用的是父类AbstractApplicationContext
的方法,此方法中进行了大量组件的初始化,其中就有我们需要的initApplicationEventMulticaster
。/**
* Initialize the ApplicationEventMulticaster.
* Uses SimpleApplicationEventMulticaster if none defined in the context.
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
可以看到其默认实现为SimpleApplicationEventMulticaster
,也是EventPublishingRunListener
的父类。
Ok,到此为止,事件分发器的初始化暂时是搞清楚了。
那么我们在调用publishEvent()
方法时到底发生了什么呢?
观察类图可以得知ApplicationContext
是继承了ApplicationEventPublisher
接口的。
/**
* Interface that encapsulates event publication functionality.
* Serves as super-interface for {@link ApplicationContext}.
*
* @author Juergen Hoeller
* @author Stephane Nicoll
* @since 1.1.1
* @see ApplicationContext
* @see ApplicationEventPublisherAware
* @see org.springframework.context.ApplicationEvent
* @see org.springframework.context.event.EventPublicationInterceptor
*/
@FunctionalInterface
public interface ApplicationEventPublisher {
/**
* Notify all matching listeners registered with this
* application of an application event. Events may be framework events
* (such as RequestHandledEvent) or application-specific events.
* @param event the event to publish
* @see org.springframework.web.context.support.RequestHandledEvent
*/
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
/**
* Notify all matching listeners registered with this
* application of an event.
* If the specified {@code event} is not an {@link ApplicationEvent},
* it is wrapped in a {@link PayloadApplicationEvent}.
* @param event the event to publish
* @since 4.2
* @see PayloadApplicationEvent
*/
void publishEvent(Object event);
}
接口定义找到了,那么其实现是在哪里呢?
由于SpringApplication的run方法直接返回的ConfigurableApplicationContext
是可以直接推送事件的,因此我们直接按照类图从它的子类中找就好了,还是熟悉的AbstractApplicationContext
。
好的,柳暗花明之后,终于可以观察以下真正的推送逻辑了。
/**
* Publish the given event to all listeners.
* @param event the event to publish (may be an {@link ApplicationEvent}
* or a payload object to be turned into a {@link PayloadApplicationEvent})
* @param eventType the resolved event type, if known
* @since 4.2
*/
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
在这里可以看到其使用的事件分发器确实是SimpleApplicationEventMulticaster
,进而调用了multicastEvent方法进而完成事件分发。
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
注意: 从代码可以看到ApplicationListener
是支持异步执行的,否则的话就会使用单一线程循环推送。如果某个ApplicationListener
耗时过长是会导致推送延迟的。因此为了性能考虑最好还是使用线程池。