Spring 事件机制源码分析

前言

上一篇 Spring 事件机制概述 的文章中,从观察者模式、Java 事件机制、Spring 事件机制的具体代码实现进行了简要的分析。本篇文章将在其基础上对 Spring 事件机制的源码进行分析。

  • Spring 事件机制流程回顾
    1. 创建一个具体的事件类,该类需继承 ApplicationEvent;
    2. 创建一个针对某个特定时间的监听器实现 ApplicationListener,并配置 @Component 注解,确保 Ioc 容器启动时,监听器会注入至 Ioc 容器;
    3. 初始化 Ioc 容器;
    4. 由于 ApplicationContext 实现了 ApplicationEventPublisher,因此直接使用 ApplicationContext 作为事件发布器发布某个事件,此时该事件的监听器便会接收到事件并做出相应的处理。此处也可以通过实现 ApplicationEventPublisherAware 接口,来获得事件发布器。

上述的流程中,可能会有这样一些疑问:

  1. 事件监听器是何时被注入的?
  2. 事件发布器是怎么样对具体的事件进行发布?

带着这两个疑问,开始源码的分析。

Spring 事件机制源码分析

在实际应用的代码中,Ioc 容器 ApplicationContext 创建完成后,监听器 ApplicationListener 及发布器 ApplicationEventPublisher 均已就绪,可直接使用进行事件发布,故从 ApplicationContext 初始化着手来分析。 通过对 Spring Ioc 的源码分析,我们知道了容器初始化的核心方法为 AbstractApplicationContext::refresh,查看 refresh 方法,我们发现有两个方法与 Spring 的事件相关。

  1. initApplicationEventMulticaster 方法

    • ApplicationEventMulticaster(事件多播器)
      ApplicationEventMulticaster 接口的方法主要是操作 ApplicationListener, 广播 ApplicationEvent

      public interface ApplicationEventMulticaster {
          void addApplicationListener(ApplicationListener<?> listener);
          void addApplicationListenerBean(String listenerBeanName);
          void removeApplicationListener(ApplicationListener<?> listener);
          void removeApplicationListenerBean(String listenerBeanName);
          void removeAllListeners();
          void multicastEvent(ApplicationEvent event);
          void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
      }
      

      分析其继承关系,在抽象类 AbstractApplicationEventMulticaster 中实现了接口的方法,SimpleApplicationEventMulticaster 中引入异步操作的支持。相关源码会在后面串联分析。


      ApplicationEventMulticaster
    • 源码
      源码的核心流程是,先判断 BeanFactory 中有没有 ApplicationEventMulticaster 类,若有则赋值给本地变量,若无则创建 SimpleApplicationEventMulticaster 并赋值给本地变量。

      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.isDebugEnabled()) {
                  logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
              }
          }
          else {
              this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
              beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
              if (logger.isDebugEnabled()) {
                  logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
                          APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                          "': using default [" + this.applicationEventMulticaster + "]");
              }
          }
      }
      
  2. registerListeners

    • 源码分析
      从 registerListeners 源码中可以看到,该方法中只是将 ApplicationListener 对应的 BeanName 保存起来了,因此这个时候 Bean 都还没有完成初始化,只有 beanDefinition 的信息,后续在完成 Bean 初始化后,会调用一个后置处理器 ApplicationListenerDetector 的 postProcessAfterInitialization 方法,将 ApplicationListener 对应的 Bean 实例绑定到 ApplicationEventMulticaster 中。
    protected void registerListeners() {
        // Register statically specified listeners first.
        // getApplicationListeners 方法中返回本地的 applicationListeners
        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!
        // 从 BeanFactory 中找到 ApplicationListener 类所对应的所有 BeanName
        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 (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }
    

在完成 ApplicationEventMulticaster 初始化,监听器注入后,后续就是如何发布事件,从 ApplicationContext 的类继承关系中知道,该类继承了 ApplicationEventPublisher,在 AbstractApplicationContext 类中实现了方法 publishEvent,具体源码为:

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    }

    // 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 {
        // 使用事件广播器广播该事件
        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);
        }
    }
}

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // getApplicationListeners 根据 event 的类型找到相应的 listener
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 增加了对异步事件的支持,如果 executor 不为空则异步通知该事件
        Executor executor = getTaskExecutor();
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}

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 {
        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.isDebugEnabled()) {
                logger.debug("Non-matching event type for listener: " + listener, ex);
            }
        }
        else {
            throw ex;
        }
    }
}
总结

从上述的分析中可知,Spring 事件发布的主要流程为:

  1. 初始化 事件多播器(ApplicationEventMulticaster)
  2. 注册 ApplicationListener
  3. 调用后置处理器 ApplicationListenerDetector 完成 ApplicationEventMulticaster 中 listener 实例的赋值;
  4. 发布事件时,调用 ApplicationEventMulticaster 的广播方法,将 Event 广播至对应的 Listener。

Spring 提供了以下 5 中标准的事件,我们可以注册响应的监听器进行处理该事件。

  1. 上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
  2. 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
  3. 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
  4. 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
  5. 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容