Spring IoC 源码系列(三)Spring 事件发布机制原理分析

旧城等待, 2023-07-14 13:00 144阅读 0赞

在 IoC 容器启动流程中有一个 finishRefresh 方法,具体实现如下:

  1. protected void finishRefresh() {
  2. clearResourceCaches();
  3. initLifecycleProcessor();
  4. getLifecycleProcessor().onRefresh();
  5. // 向所有监听 ContextRefreshedEvent 事件的监听者发布事件
  6. publishEvent(new ContextRefreshedEvent(this));
  7. LiveBeansView.registerApplicationContext(this);
  8. }

这里我们只关注 publishEvent 方法,这个方法用于发布 IoC 刷新完成事件,事件时如何发布的呢?下面我们一起来看一下。

一、原理分析

  1. @Override
  2. public void publishEvent(ApplicationEvent event) {
  3. publishEvent(event, null);
  4. }
  5. protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
  6. Assert.notNull(event, "Event must not be null");
  7. // Decorate event as an ApplicationEvent if necessary
  8. ApplicationEvent applicationEvent;
  9. // 根据事件类型进行包装
  10. if (event instanceof ApplicationEvent) {
  11. applicationEvent = (ApplicationEvent) event;
  12. } else {
  13. applicationEvent = new PayloadApplicationEvent<>(this, event);
  14. if (eventType == null) {
  15. eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
  16. }
  17. }
  18. // Multicast right now if possible - or lazily once the multicaster is initialized
  19. if (this.earlyApplicationEvents != null) {
  20. this.earlyApplicationEvents.add(applicationEvent);
  21. } else {
  22. // 获取 ApplicationEventMulticaster,将事件广播出去
  23. getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
  24. }
  25. // Publish event via parent context as well...
  26. // 判断是否存在父容器,如果存在则将事件也发布到父容器的监听者
  27. if (this.parent != null) {
  28. if (this.parent instanceof AbstractApplicationContext) {
  29. ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
  30. } else {
  31. this.parent.publishEvent(event);
  32. }
  33. }
  34. }

通过上面方法可以看出 Spring 的事件是通过 ApplicationEventMulticaster 广播出去的,这个 ApplicationEventMulticaster 在 IoC 启动流程 initApplicationEventMulticaster 方法中初始化。如果该容器还存在父容器,那也会以同样的形式将事件发布给父容器的监听者。

  1. @Override
  2. public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
  3. // 解析事件类型
  4. ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
  5. // 根据事件与事件类型获取所有监听者
  6. for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  7. // 获取异步执行器
  8. Executor executor = getTaskExecutor();
  9. if (executor != null) {
  10. // 如果执行器部位 null,则异步执行将事件发布给每一个监听者
  11. executor.execute(() -> invokeListener(listener, event));
  12. }
  13. else {
  14. // 同步发布事件
  15. invokeListener(listener, event);
  16. }
  17. }
  18. }

发布事件前会先获取所有已注册的监听器,而监听器早已在 IoC 启动流程的 registerListeners 方法中注册。获取到所有事件监听器之后,就可以进行事件发布了。发布的时候分为异步执行与顺序执行,默认情况下 executor 是没有初始化的,因此是顺序执行。

  1. protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
  2. // 获取错误处理机制
  3. ErrorHandler errorHandler = getErrorHandler();
  4. if (errorHandler != null) {
  5. try {
  6. // 事件发布
  7. doInvokeListener(listener, event);
  8. }
  9. catch (Throwable err) {
  10. errorHandler.handleError(err);
  11. }
  12. }
  13. else {
  14. doInvokeListener(listener, event);
  15. }
  16. }
  17. private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
  18. try {
  19. // 执行监听器的 onApplicationEvent 方法
  20. listener.onApplicationEvent(event);
  21. }
  22. catch (ClassCastException ex) {
  23. String msg = ex.getMessage();
  24. if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
  25. // Possibly a lambda-defined listener which we could not resolve the generic event type for
  26. // -> let's suppress the exception and just log a debug message.
  27. Log logger = LogFactory.getLog(getClass());
  28. if (logger.isTraceEnabled()) {
  29. logger.trace("Non-matching event type for listener: " + listener, ex);
  30. }
  31. }
  32. else {
  33. throw ex;
  34. }
  35. }
  36. }

到这里 Spring 的事件通知机制流程就结束了,总的来说还是比较好理解的。

二、事件通知 demo

了解了事件通知机制的基本原理后,下面我们来写个 demo 体验一下监听器是如何使用的。参考自:Event事件通知机制

  1. // 定义一个 Event
  2. public class EventDemo extends ApplicationEvent {
  3. private static final long serialVersionUID = -8363050754445002832L;
  4. private String message;
  5. public EventDemo(Object source, String message) {
  6. super(source);
  7. this.message = message;
  8. }
  9. public String getMessage() {
  10. return message;
  11. }
  12. }
  13. // 定义一个监听器1
  14. public class EventDemo1Listener implements ApplicationListener<EventDemo> {
  15. public void onApplicationEvent(EventDemo event) {
  16. System.out.println(this + " receiver " + event.getMessage());
  17. }
  18. }
  19. // 定义一个监听器2
  20. public class EventDemo2Listener implements ApplicationListener<EventDemo> {
  21. public void onApplicationEvent(EventDemo event) {
  22. System.out.println(this + " receiver " + event.getMessage());
  23. }
  24. }
  25. // 定义一个事件发布者
  26. public class EventDemoPublish {
  27. public void publish(ApplicationEventPublisher applicationEventPublisher, String message) {
  28. EventDemo eventDemo = new EventDemo(this, message);
  29. applicationEventPublisher.publishEvent(eventDemo);
  30. }
  31. }

在 XML 中配置 bean:

  1. <bean id="eventDemoPublish" class="com.jas.mess.event.EventDemoPublish"/>
  2. <bean id="eventDemo1Listener" class="com.jas.mess.event.EventDemo1Listener"/>
  3. <bean id="eventDemo2Listener" class="com.jas.mess.event.EventDemo2Listener"/>

编写测试类:

  1. @Test
  2. public void eventTest() {
  3. ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
  4. applicationContext.getBean("eventDemoPublish", EventDemoPublish.class).publish(applicationContext, "hello world");
  5. }

控制台输出:
在这里插入图片描述
不知道你有没有注意到,在发布事件的时候我们传的发布者是 applicationContextapplicationContext 本身继承自 ApplicationEventPublisher 接口,因此它本身也是一个事件发布者。

参考阅读

Event事件通知机制 by wangqi

发表评论

表情:
评论列表 (有 0 条评论,144人围观)

还没有评论,来说两句吧...

相关阅读