Spring5源码12-监听器原理

841 阅读4分钟

欢迎大家关注 github.com/hsfxuebao ,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈

1. 监听器的简单使用

自定义监听事件:

/**
 * 自定义监听事件,需要实现序列化接口
 */
public class DemoEvent  extends ApplicationEvent implements Serializable {

   private static final long serialVersionUID = 0L;

    private String msg;
    public DemoEvent(Object source) {
        super(source);
    }

    public DemoEvent(Object source, String msg) {
        super(source);
        this.msg = msg;
    }

    public void printMsg(){
        System.out.println("msg = " + msg);
    }
}
/**
 * 事件需要实现序列化接口
 */
public class MessageEvent extends ApplicationEvent implements Serializable {
   private static final long serialVersionUID = 0L;

   public MessageEvent(String source) {
      super(source);
   }

   @Override
   public String toString() {
      return "MessageEvent{" +
            ", source=" + source +
            '}';
   }
}

自定义监听器:

/**
 * 自定义监听器
 */
@Component
public class DemoListener implements ApplicationListener<DemoEvent> {


    @Override
    public void onApplicationEvent(DemoEvent event) {
       System.out.println("DemoListener onApplicationEvent run");
        event.printMsg();
    }
}

@Component
public class MessageEventListener implements ApplicationListener<MessageEvent> {

   @Override
   public void onApplicationEvent(MessageEvent event) {
      System.out.println("MessageEventListener onApplicationEvent run");
   }
}

@Component
public class MessageEventTwoListener implements ApplicationListener<MessageEvent> {

   @Override
   public void onApplicationEvent(MessageEvent event) {
      System.out.println("MessageEventTwoListener onApplicationEvent run");
   }
}

测试:

public class AnnotationMainTest {

   public static void main(String[] args) {

      AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(MainConfig.class);
      //测试事件监听器
      DemoEvent demoEvent = new DemoEvent("", "world");
      context.publishEvent(demoEvent);
      context.publishEvent(new MessageEvent(""));
  }
}

测试结果:

image.png

当程序运行时,Spring 会按照顺序,先发出 DemoEvent 事件转给DemoListener 监听器,进行进一步的处理。在发送MessageEvent事件转给MessageEventTwoListenerMessageEventListener监听器,进一步处理。

实际上,Spring是将所有的事件都发送给所有的监听者,这一点在后面的源码分析中可以看出来。

2.源码分析

整个监听事件的流程其实很简单

2.1 初始化 事件广播器 - initApplicationEventMulticaster();

initApplicationEventMulticaster 的方法比较简单,考虑了两种情况

  • 如果用户自定义了事件广播器,在使用用户自定义的事件广播器
  • 如果用户没有自定义事件广播器,则使用默认的 ApplicationEventMulticaster
// 如果用户自定义了事件广播器,在使用用户自定义的事件广播器
// 如果用户没有自定义事件广播器,则使用默认的 ApplicationEventMulticaster
protected void initApplicationEventMulticaster() {
   // 注册 ApplicationEventMulticaster 组件
   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 + "]");
      }
   }
   // 否则使用默认的事件广播器 SimpleApplicationEventMulticaster
   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() + "]");
      }
   }
}

上面的代码看起来很简单,一般情况下我们都是使用默认的事件广播器SimpleApplicationEventMulticaster

2.2 注册监听器 - registerListeners();

protected void registerListeners() {
   // 把所有监听器保存到多播器的集合中
   // Register statically specified listeners first.
   // 硬编码方式注册的监听器处理
   for (ApplicationListener<?> listener : getApplicationListeners()) {
      // 保存到多播器中
      getApplicationEventMulticaster().addApplicationListener(listener);
   }

   // Do not initialize FactoryBeans here: We need to leave all regular beans
   // uninitialized to let post-processors apply to them!
   // 获取ApplicationListener 在ioc容器中注册的bean的名字
   // 配置文件注册的监听处理器
   String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
   for (String listenerBeanName : listenerBeanNames) {
      // 获取所有的监听器,并保存他们的名字在
      getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
   }

   // Publish early application events now that we finally have a multicaster...
   // 派发之前的一些早期事件
   // 发布之前保存的需要发布的事件
   Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
   this.earlyApplicationEvents = null;
   if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
      for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
         // todo
         getApplicationEventMulticaster().multicastEvent(earlyEvent);
      }
   }
}

2.3 事件的发布 - publishEvent

我们在 上面的Demo 中, 通过 context.publishEvent(demoEvent); 发布的事件最终会执行如下方法:

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;
   // ApplicationEvent 接口下的事件
   if (event instanceof ApplicationEvent) {
      applicationEvent = (ApplicationEvent) event;
   }
   else {
      // 任意对象作为事件最终被封装到了 PayloadApplicationEvent
      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 {
      // 获取事件广播器进行广播事件广播。我们上面说了一般我们使用默认的事件广播器即 SimpleApplicationEventMulticaster
      // 拿到多播器发送事件即可
      getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
   }

   // Publish event via parent context as well...
   // 寻找父类BeanFactory,继续发布消息
   if (this.parent != null) {
      if (this.parent instanceof AbstractApplicationContext) {
         ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
      }
      else {
         this.parent.publishEvent(event);
      }
   }
}

我们继续看 multicastEvent 方法如下,可以看到,当Spring事件产生的时候,默认会使用SimpleApplicationEventMulticaster#multicastEvent 方法来广播事件,遍历所有的监听器,并使用监听器中的 onApplicationEvent 方法来进行监听事件的处理。而对于每个监听器来说,其实都可以获取到产生的事件,但使用进行处理由监听器自己决定。

// 事件派发 可以是异步的
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();
   // 观察者模式,把关注event事件的 所有监听器拿来,调用他们的onApplicationEvent()即可
   // 获取匹配事件类型的事件事件监听器。
   // 比如上面Demo中DemoListener 监听的是DemoEvent 事件类型,这里就是筛选出接受 DemoEvent 类型的监听器。
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      if (executor != null) {
         //  在invokeListener 方法中激活调用事件监听器的onApplicationEvent方法
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
         invokeListener(listener, event);
      }
   }
}

3. 框图

image.png

参考文章

Spring5源码注释github地址
Spring源码深度解析(第2版)
spring源码解析
Spring源码深度解析笔记
Spring注解与源码分析
Spring注解驱动开发B站教程