Spring框架的ApplicationListener事件监听机制
spring中的事件监听机制依托于ApplicationEvent 事件类和ApplicationListener 接口,对于实现了ApplicationListener 接口的子类并且注入到容器中,每当有ApplicationEvent 事件被发布到springContext中,Listener都会被通知,从本质上来讲,这是一个观察者模式的实现。
下面我们以一个自定义例子跟踪下事件监听机制的原理。
首先自定一个一个Listener实现ApplicationListener 接口并注入到容器。
@Component
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
logger.info("发现事件。。。" + event);
}
}
为方便断点,例子中监听的是ContextRefreshedEvent,容器刷新事件,容器刷新完成会发布该事件,是ApplicationEvent的子类。对上面onApplicationEvent方法打断点,通过跟踪方法的调用栈可以看到调用流程:
ContextRefreshedEvent事件发布流程:
这张图可以看到具体的调用流程,其中比较重要的是第5步和第8步,我们来分别分析下主要代码。
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 获取容器中的Executor执行器
Executor executor = getTaskExecutor();
// 获取所有的ApplicationListener及其子类,并循环处理
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
// 如果容器中有Executor执行器就使用异步的方式调用listener方法。
executor.execute(() -> invokeListener(listener, event));
}
else {
// 如果没有Executor执行器就同步调用。
invokeListener(listener, event);
}
}
}
首先我们看到第三步发布事件的时候新建了一个ContextRefreshedEvent对象。然后第5步获取ApplicationEventMulticaster(事件多播器/事件发布器),接着调用它的multicastEvent方法。上面是该方法的具体内容。主要就是获取容器中所有的ApplicationListener及其子类,然后调用invokeListener方法,也就是上图中的第6步。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 执行ApplicationListener的onApplicationEvent方法
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
上面是invokeListener中调用的doInvokeListener。拿到一个ApplicationListener然后执行他的onApplicationEvent方法,这个方法就是我们自定义实现ApplicationListener接口需要覆盖的方法。然后方法就执行到了我们覆盖的方法内,就是上面我打了一行日志的地方。
所以整个流程总结下来比较重要的部分就是首先容器刷新创建了一个ContextRefreshedEvent事件到容器中,然后获取容器中的事件多播器,紧接着在事件多播器内部获取所有的事件监听器(ApplicationListtener)并回调他们的onApplicationEvent方法。那到现在我们应该能清楚的看到在事件监听机制中设计模式的应用-观察者模式。
观察者模式是一种行为设计模式,观察者模式涉及到两个角色即publisher和subscribers,publisher管理某些数据,当publisher内的数据改变或事件发生时,通知subscribers。
这里是观察者模式的结构图,其中EventManager作为事件发布者,它提供了管理观察者的方法,比如增加删除方法,目的是方便观察者根据自己的需要选择监听该事件或者不监听。而EventListener作为观察者需要向发布者提供类似update方法的功能,当发布者状态发生变化或发生某种事件时获取所有观察者并回调他们的update方法。
在本例中事件多播器作为发布者,它能够获得所有的观察者-ApplicationListener。当容器中状态发生变化即事件多播器的publishEvent方法被调用 ,就获取所有的Listener并回调他们的onApplicationEvent方法,这个方法和上面的update方法功能类似。
到目前为止spring中的事件监听机制就分析完了。但是还存在两个问题,1、事件多播器是怎么添加到容器中的,什么时候添加的?2、ApplicatioinListener是何时何地添加到容器中的?接下来我们针对上述两个问题展开分析。
首先,事件多播器是何时何地添加到容器中的?
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
initMessageSource();
// 初始化事件多播器
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
这里是容器refresh方法。容器刷新的核心方法。刚才我们的断点是在这个方法里的finishRefresh()进入的,而在这个方法执行之前我们看到有一个initApplicationEventMulticaster();方法,初始化事件多播器。来看下该方法逻辑:
protected void initApplicationEventMulticaster() {
// 获取容器
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 如果容器中有名字是applicationEventMulticaster的组件,getBean初始化。
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 + "]");
}
}
// 否则创建一个SimpleApplicationEventMulticaster并注入到容器中。
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() + "]");
}
}
}