Spring中的事件发布机制原理解析
Spring事件发布机制原理
在 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 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 接口,因此它本身也是一个事件发布者。
到此这篇关于Spring中的事件发布机制原理解析的文章就介绍到这了,更多相关Spring事件发布机制原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
SpringCloud Config分布式配置中心使用教程介绍
springcloud config是一个解决分布式系统的配置管理方案。它包含了 client和server两个部分,server端提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client端通过接口获取数据、并依据此数据初始化自己的应用2022-12-12拦截器获取request的值之后,Controller拿不到值的解决
这篇文章主要介绍了拦截器获取request的值之后,Controller拿不到值的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2021-10-10
最新评论