ApplicationListener源码解读(异步通知、统一ErrorHandler)

937 阅读9分钟

本文正在参加「金石计划」

简单使用(老司机直接跳过~)

定义一个实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
​
   private Integer age;
​
   private String name;
​
}

定义一个事件,需要继承ApplicationEvent

@Getter
@Setter
public class CustomEvent extends ApplicationEvent {
​
   private UserInfo userInfo;
​
   public CustomEvent(UserInfo userInfo) {
      super(userInfo);
      this.userInfo = userInfo;
   }
​
}

定义一个监听器,需要实现ApplicationListener接口

@Component
public class CustomListener implements ApplicationListener<CustomEvent> {
​
   @Override
   public void onApplicationEvent(@NonNull CustomEvent event) {
      System.out.println("CustomListener接收到事件~");
      System.out.println(JSONObject.toJSONString(event.getUserInfo()));
   }
​
}

测试

@SpringBootTest
public class ListenerTest {
​
  @Resource
  private ApplicationContext applicationContext;
​
  @Test
  public void test() {
    UserInfo userInfo = new UserInfo(1, "1");
    // 发布事件
    applicationContext.publishEvent(new CustomEvent(userInfo));
  }
​
}

可见,监听器成功监听并处理了我们发布的事件~

image-20230312135023339


源码分析

通过上面的案例,我们了解到,发布事件是调用的publishEvent方法,所以我们直接从publishEvent开始看~

publishEvent

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
   Assert.notNull(event, "Event must not be null");
​
   // 如果event是ApplicationEvent实例,则向上转型
   ApplicationEvent applicationEvent;
   if (event instanceof ApplicationEvent) {
      applicationEvent = (ApplicationEvent) event;
   }
   else {
      applicationEvent = new PayloadApplicationEvent<>(this, event);
      if (eventType == null) {
         eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
      }
   }
​
   // 早期发布的事件,此时listener还未注册,所以先将event存起来
   if (this.earlyApplicationEvents != null) {
      this.earlyApplicationEvents.add(applicationEvent);
   }
   else {
      // 获取事件发布器,并进行发布
      getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
   }
​
   // 如果父context不为空,则要通过父context再发布一次
   if (this.parent != null) {
      if (this.parent instanceof AbstractApplicationContext) {
         ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
      }
      else {
         this.parent.publishEvent(event);
      }
   }
}

publishEvent方法,核心逻辑在于获取时间发布器进行发布,即下面这行代码

getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);


applicationEventMulticaster(事件多播器的获取和初始化)

getApplicationEventMulticaster只是简单的获取applicationEventMulticaster,并做一下参数校验,没什么好说的。

@Nullable
private ApplicationEventMulticaster applicationEventMulticaster;
​
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
   if (this.applicationEventMulticaster == null) {
      throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
            "call 'refresh' before multicasting events via the context: " + this);
   }
   return this.applicationEventMulticaster;
}

重点在于,applicationEventMulticaster是怎么初始化的?

熟悉Spring源码的小伙伴们,对下面这张图肯定不会陌生~

没错,正是Spring初始化过程中非常重要的refresh方法 ,在refresh方法中,会调用initApplicationEventMulticaster方法初始化事件多播器

image-20230312204515795

initApplicationEventMulticaster源码如下:

public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
​
protected void initApplicationEventMulticaster() {
   // 获取bean工厂 
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   
   // 检查IOC容器中是否已经有 beanName = applicationEventMulticaster的bean了
   // 如果存在则直接使用已有的就行
   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 {
      // 如果不存在,则new一个SimpleApplicationEventMulticaster,并注册到IOC容器中
      this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
      
      // 作为单例Bean注册到IOC容器中
      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() + "]");
      }
   }
}

initApplicationEventMulticaster中逻辑很简单

  1. 如果IOC容器中是否已经有beanName = applicationEventMulticaster的bean了,则直接使用
  2. 如果容器里没有,则new一个SimpleApplicationEventMulticaster作为事件多播器,并注册到IOC容器中.

这里比较重点,我们可以自定义applicationEventMulticaster,这样事件发布就会走我们自定义的发布器了

关于这里,文末会给出案例


multicastEvent

ok,了解到事件多播器的获取初始化逻辑之后,我们接着继续来看事件的发布

// 获取事件发布器,并进行事件发布
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
       // 解析event的类型
       ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
       // 获取线程池
       Executor executor = getTaskExecutor();
       // 通过event、eventType匹配到符合的监听器
       for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
         
          // 如果线程池不为null,则通过线程池提交任务异步发布
          if (executor != null) {
             executor.execute(() -> invokeListener(listener, event));
          }
          else {
             // 否则,同步发布
             invokeListener(listener, event);
          }
       }
    }

multicastEvent的逻辑也很简单,我们简单总结一下

  1. 通过eventeventType匹配到所有符合的事件监听器进行发布
  2. 事件的发布可以同步也可以异步,但默认是同步

划重点了!

Spring的ApplicationListener观察者模式,默认是同步的!!!

还记得上面我们分析的,事件多播器初始化逻辑吗?

默认是new SimpleApplicationEventMulticaster(),并不会设置taskExecutor

image-20230312205611866

文末会给出异步发布模式,这里就不过多介绍了~

getApplicationListeners(获取符合event的监听器)

final Map<ListenerCacheKey, CachedListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
    ​
    protected Collection<ApplicationListener<?>> getApplicationListeners(
          ApplicationEvent event, ResolvableType eventType) {
    ​
       // 获取event Class
       Object source = event.getSource();
       Class<?> sourceType = (source != null ? source.getClass() : null);
      
       // 将事件class和类型封装在ListenerCacheKey中,并将其作为缓存的key
       // 并且重写了equals方法,会去比较event的Class和类型
       ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
    ​
       CachedListenerRetriever newRetriever = null;
    ​
       // 从缓存中获取,如果没有则新建CachedListenerRetriever
       // CachedListenerRetriever里会存放着符合该event的所有监听器
       CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
       if (existingRetriever == null) {
          if (this.beanClassLoader == null ||
                (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
                      (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
             newRetriever = new CachedListenerRetriever();
            
             // putIfAbsent 如果缓存中不存在对应key才会进行put
             // 会返回老的value值,所以第一次会返回null
             existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
             if (existingRetriever != null) {
                newRetriever = null;  // no need to populate it in retrieveApplicationListeners
             }
          }
       }
    ​
       if (existingRetriever != null) {
          // 如果缓存中已经存在,则直接取出该事件对应的监听器返回
          Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
          if (result != null) {
             return result;
          }
       }
    ​
       // 解析event对应的监听器
       return retrieveApplicationListeners(eventType, sourceType, newRetriever);
    }

上面的源码还没有涉及到匹配逻辑,只是做的一层缓存(ConcurrentHashMap)优化~

首先,会将eventClassType封装在ListenerCacheKey中,作为缓存的key

ListenerCacheKey中也重写了equalse方法,会比较eventTypesourceType

private final ResolvableType eventType;
    ​
    @Nullable
    private final Class<?> sourceType;
    ​
    public ListenerCacheKey(ResolvableType eventType, @Nullable Class<?> sourceType) {
      Assert.notNull(eventType, "Event type must not be null");
      this.eventType = eventType;
      this.sourceType = sourceType;
    }
    ​
    public boolean equals(@Nullable Object other) {
       if (this == other) {
          return true;
       }
       if (!(other instanceof ListenerCacheKey)) {
          return false;
       }
       ListenerCacheKey otherKey = (ListenerCacheKey) other;
       return (this.eventType.equals(otherKey.eventType) &&
             ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType));
    }

其次就比较简单了,如果缓存中有就用缓存的,缓存中没有就新建一个CachedListenerRetriever,并放入缓存里

这里有个小细节,往缓存里添加数据用的是putIfAbsent方法,也就是key不存在才会添加数据,并且返回的是oldValue

也就是说,当事件第一次发布时,缓存里没有,putIfAbsent会返回null,即existingRetriever = null

则,下面的if判断,第一次是false,还是会走retrieveApplicationListeners

existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
    ​
    if (existingRetriever != null) {
      // 如果缓存中已经存在,则直接取出该事件对应的监听器返回
      Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
      if (result != null) {
        return result;
      }
    }
    ​
    // 解析event对应的监听器
    return retrieveApplicationListeners(eventType, sourceType, newRetriever);

retrieveApplicationListeners(监听器匹配)

private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();
    ​
    private Collection<ApplicationListener<?>> retrieveApplicationListeners(
          ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {
    ​
       // 存放event对应的所有监听器实例
       List<ApplicationListener<?>> allListeners = new ArrayList<>();
      
       // retriever != null说明是第一次event匹配,需要解析
       Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
       // 存放event对应的监听器beanName
       Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);
    ​
       Set<ApplicationListener<?>> listeners;
       Set<String> listenerBeans;
      
       // defaultRetriever存放了所有的监听器实例及beanName
       synchronized (this.defaultRetriever) {
          listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
          listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
       }
    ​
       // 遍历所有监听器实例,检索出event对应的监听器实例
       for (ApplicationListener<?> listener : listeners) {
          // 是否能作为当前event的监听器
          if (supportsEvent(listener, eventType, sourceType)) {
             if (retriever != null) {
                filteredListeners.add(listener);
             }
             allListeners.add(listener);
          }
       }
    ​
       // 遍历所有监听器beanName,检索出event对应的监听器beanName
       if (!listenerBeans.isEmpty()) {
          // 获取bean工厂
          ConfigurableBeanFactory beanFactory = getBeanFactory();
          for (String listenerBeanName : listenerBeans) {
             try {
                // 是否能作为当前event的监听器
                if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
                   // 获取监听器bean
                   ApplicationListener<?> listener =
                         beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                  
                   // 如果listener不在改event对应的监听器集合中,但又符合该event
                   if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                      if (retriever != null) {
                         // 这块if-else没太懂,有没有大佬指点!!!
                         if (beanFactory.isSingleton(listenerBeanName)) {
                            filteredListeners.add(listener);
                         }
                         else {
                            filteredListenerBeans.add(listenerBeanName);
                         }
                      }
                      allListeners.add(listener);
                   }
                }
                else {
                   Object listener = beanFactory.getSingleton(listenerBeanName);
                   if (retriever != null) {
                      filteredListeners.remove(listener);
                   }
                   allListeners.remove(listener);
                }
             }
             catch (NoSuchBeanDefinitionException ex) {
             }
          }
       }
    ​
       // 排序,根据@Order注解,或者Ordered接口定义的优先级排序,值越小优先级越大
       AnnotationAwareOrderComparator.sort(allListeners);
      
       if (retriever != null) {
          // 将event对应的监听器缓存起来
          if (filteredListenerBeans.isEmpty()) {
             retriever.applicationListeners = new LinkedHashSet<>(allListeners);
             retriever.applicationListenerBeans = filteredListenerBeans;
          }
          else {
             retriever.applicationListeners = filteredListeners;
             retriever.applicationListenerBeans = filteredListenerBeans;
          }
       }
       return allListeners;
    }

上面源码一句话可以概括,获取到所有listener遍历,根据eventType, sourceType匹配到符合当前event的监听器

但其中会有些小细节

  1. defaultRetriever存放了所有的监听器实例及beanName
  2. retriever != null说明event是第一次来解析对应的监听器

invokeListener(通知对应的监听器)

获取event对应监听器分析完了,接下来就回过头来分析通知监听器的源码了~

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
      // 获取error的处理器
      ErrorHandler errorHandler = getErrorHandler();
      
      // 如果存在errorHandler,则对doInvokeListener进行try-catch
      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()) ||
            (event instanceof PayloadApplicationEvent &&
             matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
          Log loggerToUse = this.lazyLogger;
          if (loggerToUse == null) {
            loggerToUse = LogFactory.getLog(getClass());
            this.lazyLogger = loggerToUse;
          }
          if (loggerToUse.isTraceEnabled()) {
            loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
          }
        }
        else {
          throw ex;
        }
      }
    }

invokeListener的内容也比较简单,就是调用对应监听器实现onApplicationEvent方法,实现通知效果~

image-20230312223052317


扩展知识点


实现异步事件通知

在上面探究applicationEventMulticaster初始化源码时,我提到了,Spring的观察者模式默认是同步的,如下图所示

@Component
    public class CustomListener implements ApplicationListener<CustomEvent> {
    ​
       @Override
       public void onApplicationEvent(@NonNull CustomEvent event) {
          System.out.println(Thread.currentThread().getName() + " CustomListener接收到事件~");
          System.out.println(JSONObject.toJSONString(event.getUserInfo()));
       }
    ​
    }

image-20230312231007370

但是其底层也是支持通过线程池进行异步通知

那么该如何做呢?

在上面的applicationEventMulticaster初始化源码中,我们也能看到,如果容器中有beanName = applicationEventMulticaster的bean,就会将其作为事件多播器,所以我们可以自定义一个applicationEventMulticaster

@Configuration
    public class SpringConfig {
    ​
       @Bean(name = "applicationEventMulticaster")
       public ApplicationEventMulticaster applicationEventMulticaster() {
          SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
          simpleApplicationEventMulticaster.setTaskExecutor(Executors.newFixedThreadPool(10, new DefaultThreadFactory("custom")));
          return simpleApplicationEventMulticaster;
       }
    ​
    }

此时再次测试事件发布,可以看到打印的当前线程不是main线程了,而是使用我们自定义的线程池中的线程

image-20230312231822894


自定义监听器执行异常处理器

如果,我们项目中定义了许多的监听器,那么为了避免在处理事件的时候出现异常,我们难免都会去进行**try-catch,会写出很多冗余代码。**

那么怎么解决呢?

其实,在上面分析invokeListener源码的时候我们也能直接看到,可以支持统一的ErrorHandler而且对代码没有侵入性~

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
      // 获取error的处理器
      ErrorHandler errorHandler = getErrorHandler();
      
      // 如果存在errorHandler,则对doInvokeListener进行try-catch
      if (errorHandler != null) {
        try {
          // 通知监听器
          doInvokeListener(listener, event);
        }
        catch (Throwable err) {
          errorHandler.handleError(err);
        }
      }
      else {
        doInvokeListener(listener, event);
      }
    }

自定义ErrorHandler

@Slf4j
    public class CustomErrorHandler implements ErrorHandler {
    ​
       @Override
       public void handleError(@NonNull Throwable throwable) {
          System.out.println("自定义error处理");
          log.error("CustomErrorHandler", throwable);
       }
    ​
    }
  @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster applicationEventMulticaster() {
       SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
       simpleApplicationEventMulticaster.setTaskExecutor(Executors.newFixedThreadPool(10, new DefaultThreadFactory("custom")));
    ​
       // 设置自定义errorHandler
       simpleApplicationEventMulticaster.setErrorHandler(new CustomErrorHandler());
    ​
       return simpleApplicationEventMulticaster;
    }
 @Component
    public class CustomListener implements ApplicationListener<CustomEvent> {
    ​
       @Override
       public void onApplicationEvent(@NonNull CustomEvent event) {
          // 手动模拟异常
          int i = 1 / 0;
          System.out.println(Thread.currentThread().getName() + " CustomListener接收到事件~");
          System.out.println(JSONObject.toJSONString(event.getUserInfo()));
       }
    ​
    }

测试

image-20230312232803563


registerListeners(早期event发布、listener缓存)

refesh方法中,会调用registerListeners注册监听器,在上面的retrieveApplicationListeners源码分析中,我们也说到了,会从defaultRetriever中拿到所有listener

而此时的registerListeners就是将listener缓存到defaultRetriever

此外,还有publishEvent中提到的earlyApplicationEvents

listener还未缓存,就开始调用publishEvent了,这时先将event缓存到earlyEventsToProcess中,待缓存完毕后进行事件发布~

protected void registerListeners() {
       // 缓存listener实例
       for (ApplicationListener<?> listener : getApplicationListeners()) {
          getApplicationEventMulticaster().addApplicationListener(listener);
       }
    ​
       // 缓存listener beanName
       String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
       for (String listenerBeanName : listenerBeanNames) {
          getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
       }
    ​
       // 发布早期事件(listener还未缓存,就开始调用publishEvent了,这时先缓存到earlyEventsToProcess中)
       Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
       // 发布早期事件,并将this.earlyApplicationEvents = null;
       this.earlyApplicationEvents = null;
       if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
          for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
             getApplicationEventMulticaster().multicastEvent(earlyEvent);
          }
       }
    }
    ​
    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
      synchronized (this.defaultRetriever) {
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
          this.defaultRetriever.applicationListeners.remove(singletonTarget);
        }
        this.defaultRetriever.applicationListeners.add(listener);
        this.retrieverCache.clear();
      }
    }
    ​
    @Override
    public void addApplicationListenerBean(String listenerBeanName) {
      synchronized (this.defaultRetriever) {
        this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
        this.retrieverCache.clear();
      }
    }

上面registerListeners会调用getApplicationListeners获取所有listener,哪这里是怎么获取的呢?

答案就是: Spring中存在ApplicationListenerDetector实现了BeanPostProcessor,如果bean instanceof ApplicationListener,且是单例bean,则会添加到AbstractApplicationContext类的applicationListeners这个集合中

getApplicationListeners也是直接获取的applicationListeners

 public Object postProcessAfterInitialization(Object bean, String beanName) {
       if (bean instanceof ApplicationListener) {
          // potentially not detected as a listener by getBeanNamesForType retrieval
          Boolean flag = this.singletonNames.get(beanName);
          if (Boolean.TRUE.equals(flag)) {
             // singleton bean (top-level or inner): register on the fly
             this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
          }
          else if (Boolean.FALSE.equals(flag)) {
             if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
                // inner bean with other scope - can't reliably process events
                logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
                      "but is not reachable for event multicasting by its containing ApplicationContext " +
                      "because it does not have singleton scope. Only top-level listener beans are allowed " +
                      "to be of non-singleton scope.");
             }
             this.singletonNames.remove(beanName);
          }
       }
       return bean;
    }

结尾

我是 Code皮皮虾 ,会在以后的日子里跟大家一起学习,一起进步!

觉得文章不错的话,求一个点赞~,更多精彩内容尽在->JavaCodes