Spring 事件機制

微信公眾號_CoderLi

概念

在一個完整的事件體系中、存在以下的角色

  1. 事件:描述發生了什麼事情、比如說請求處理完成、Spring 容器刷新完畢
  2. 事件源:事件的產生者、任何一個事件都必須有一個事件源。比如請求處理完成的事件源就是 DispatcherServlet 、Spring 容器刷新完畢的事件源就是 ApplicationContext
  3. 事件廣播器:事件和事件監聽器的橋樑、負責把事件通知給事件監聽器
  4. 事件監聽器:監聽事件的發生、可以在監聽器中做一些處理

Spring 事件

微信公眾號:CoderLi

我們常見的事件可能就是 ApplicationContextEvent 、它的子類 ContextRefreshedEvent 是我們常見的事件類型、在 Spring 將所有非延遲加載的 bean 實例化之後發佈。

微信公眾號:CoderLi

再來看看 Spring 事件的體系結構

微信公眾號:CodeLi

Spring 監聽器

微信公眾號:CoderLi

事件廣播器

微信公眾號:CoderLi

微信公眾號:CoderLi

ApplicationContext 對事件的支持

微信公眾號:CoderLi

ApplicationEventPublisher 這個是 Spring 提供給用戶使用的一個事件發佈器啊、真正實現發佈功能還是委託給上面的 ApplicationEventMulticaster 去實現的。

Spring 提供了 ApplicationEventPublisherAware 讓用戶可以去獲取這個發佈器進行事件發佈。

使用方式

Spring 提供了兩種方式

  1. 實現 ApplicationListener 接口
  2. 使用註解 @EventListener

註解的實現源碼

我們直接看到 EventListenerMethodProcessor 該類實現了接口 SmartInitializingSingleton 接口、該接口會在 Spring 初始化完所有的非延遲加載的 bean 之後被調用。

public interface SmartInitializingSingleton {

   /**
    * Invoked right at the end of the singleton pre-instantiation phase,
    * with a guarantee that all regular singleton beans have been created
    * already. {@link ListableBeanFactory#getBeansOfType} calls within
    * this method won't trigger accidental side effects during bootstrap.
    * <p><b>NOTE:</b> This callback won't be triggered for singleton beans
    * lazily initialized on demand after {@link BeanFactory} bootstrap,
    * and not for any other bean scope either. Carefully use it for beans
    * with the intended bootstrap semantics only.
    */
   void afterSingletonsInstantiated();
}

微信公眾號:CoderLi

其實實現邏輯非常簡單

for (Method method : annotatedMethods.keySet()) {
   for (EventListenerFactory factory : factories) {
      if (factory.supportsMethod(method)) {
         Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
         ApplicationListener<?> applicationListener =
               factory.createApplicationListener(beanName, targetType, methodToUse);
         if (applicationListener instanceof ApplicationListenerMethodAdapter) {
            ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
         }
         context.addApplicationListener(applicationListener);
         break;
      }
   }
}

找出所有被註解修飾的方法、然後分別創建一個對應的 ApplicationListener、收到事件後反射調用該方法。

public void processEvent(ApplicationEvent event) {
   Object[] args = resolveArguments(event);
   if (shouldHandle(event, args)) {
      Object result = doInvoke(args);
      if (result != null) {
         handleResult(result);
      }
      else {
         logger.trace("No result object given - no result to handle");
      }
   }
}

監聽器調用的先後順序

我們可以在 AbstractApplicationEventMulticaster#retrieveApplicationListeners 中看到是支持我們指定監聽器的順序的、Spring 很多涉及順序的都可以使用

  1. 實現 Ordered 接口
  2. 實現 PriorityOrdered 接口
  3. 使用 @Ordered 接口

異步調用監聽器

默認情況下、Spring 創建的事件廣播器是採用同步方式調用通知監聽器的、我們可以設置或者替換 Spring 默認的監聽器來達到異步調用的目的、當然也可以擴展、根據事件的不同採用同步或者異步的方式、而不是單一的要麼所有同步要麼所有異步

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
         invokeListener(listener, event);
      }
   }
}

Spring refresh 之前回初始化事件傳播器

protected void initApplicationEventMulticaster() {
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   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 {
      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() + "]");
      }
   }
}

替換原來的事件傳播器

@Component("applicationEventMulticaster")
public class TestEventMulticaster extends SimpleApplicationEventMulticaster {
}
Tags: