在 IoC 容器启动流程中有一个 finishRefresh
方法,具体实现如下:
protected void finishRefresh() {
clearResourceCaches();
initLifecycleProcessor();
getLifecycleProcessor().onRefresh();
// 向所有监听 ContextRefreshedEvent 事件的监听者发布事件
publishEvent(new ContextRefreshedEvent(this));
LiveBeansView.registerApplicationContext(this);
}
这里我们只关注 publishEvent
方法,这个方法用于发布 IoC 刷新完成事件,事件时如何发布的呢?下面我们一起来看一下。
一、原理分析
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
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 {
// 获取 ApplicationEventMulticaster,将事件广播出去
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);
}
}
}
通过上面方法可以看出 Spring 的事件是通过 ApplicationEventMulticaster
广播出去的,这个 ApplicationEventMulticaster
在 IoC 启动流程 initApplicationEventMulticaster
方法中初始化。如果该容器还存在父容器,那也会以同样的形式将事件发布给父容器的监听者。
@Override
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) {
// 如果执行器部位 null,则异步执行将事件发布给每一个监听者
executor.execute(() -> invokeListener(listener, event));
}
else {
// 同步发布事件
invokeListener(listener, event);
}
}
}
发布事件前会先获取所有已注册的监听器,而监听器早已在 IoC 启动流程的 registerListeners
方法中注册。获取到所有事件监听器之后,就可以进行事件发布了。发布的时候分为异步执行与顺序执行,默认情况下 executor
是没有初始化的,因此是顺序执行。
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);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 执行监听器的 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;
}
}
}
到这里 Spring 的事件通知机制流程就结束了,总的来说还是比较好理解的。
二、事件通知 demo
了解了事件通知机制的基本原理后,下面我们来写个 demo 体验一下监听器是如何使用的。参考自:Event事件通知机制
// 定义一个 Event
public class EventDemo extends ApplicationEvent {
private static final long serialVersionUID = -8363050754445002832L;
private String message;
public EventDemo(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
// 定义一个监听器1
public class EventDemo1Listener implements ApplicationListener<EventDemo> {
public void onApplicationEvent(EventDemo event) {
System.out.println(this + " receiver " + event.getMessage());
}
}
// 定义一个监听器2
public class EventDemo2Listener implements ApplicationListener<EventDemo> {
public void onApplicationEvent(EventDemo event) {
System.out.println(this + " receiver " + event.getMessage());
}
}
// 定义一个事件发布者
public class EventDemoPublish {
public void publish(ApplicationEventPublisher applicationEventPublisher, String message) {
EventDemo eventDemo = new EventDemo(this, message);
applicationEventPublisher.publishEvent(eventDemo);
}
}
在 XML 中配置 bean:
<bean id="eventDemoPublish" class="com.jas.mess.event.EventDemoPublish"/>
<bean id="eventDemo1Listener" class="com.jas.mess.event.EventDemo1Listener"/>
<bean id="eventDemo2Listener" class="com.jas.mess.event.EventDemo2Listener"/>
编写测试类:
@Test
public void eventTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
applicationContext.getBean("eventDemoPublish", EventDemoPublish.class).publish(applicationContext, "hello world");
}
控制台输出:
不知道你有没有注意到,在发布事件的时候我们传的发布者是 applicationContext
,applicationContext
本身继承自 ApplicationEventPublisher
接口,因此它本身也是一个事件发布者。
参考阅读
Event事件通知机制 by wangqi