深入理解 Spring 的事件发布监听机制

1. 什么是事件监听机制

在讲解事件监听机制前,我们先回顾下设计模式中的观察者模式,因为事件监听机制可以说是在典型观察者模式基础上的进一步抽象和改进。我们可以在 JDK 或者各种开源框架比如 Spring 中看到它的身影,从这个意义上说,事件监听机制也可以看做一种对传统观察者模式的具体实现,不同的框架对其实现方式会有些许差别。

典型的观察者模式将有依赖关系的对象抽象为了观察者主题两个不同的角色,多个观察者同时观察一个主题,两者只通过抽象接口保持松耦合状态,这样双方可以相对独立的进行扩展和变化:比如可以很方便的增删观察者,修改观察者中的更新逻辑而不用修改主题中的代码。但是这种解耦进行的并不彻底,这具体体现在以下几个方面:

  • 1.抽象主题需要依赖抽象观察者,而这种依赖关系完全可以去除。
  • 2.主题需要维护观察者列表,并对外提供动态增删观察者的接口,
  • 3.主题状态改变时需要由自己去通知观察者进行更新。

我们可以把主题(Subject)替换成事件(Event),把对特定主题进行观察的观察者(Observer)替换成对特定事件进行监听的监听器(EventListener),而把原有主题中负责维护主题与观察者映射关系以及在自身状态改变时通知观察者的职责从中抽出,放入一个新的角色事件发布器(EventPublisher)中,事件监听模式的轮廓就展现在了我们眼前,如下图所示:



常见事件监听机制的主要角色如下:

  • 事件:对应于观察者模式中的主题。
  • 事件监听器:对应于观察者模式中的观察者。监听器监听特定事件,并在内部定义了事件发生后的响应逻辑。
  • 事件发布器:事件监听器的容器,对外提供发布事件和增删事件监听器的接口,维护事件和事件监听器之间的映射关系,并在事件发生时负责通知相关监听器。

Spring 框架对事件的发布与监听提供了相对完整的支持,它扩展了JDK中对自定义事件监听提供的基础框架,并与 Spring 的 IOC 特性作了整合,使得用户可以根据自己的业务特点进行相关的自定义,并依托 Spring 容器方便的实现监听器的注册和事件的发布。

2. Spring 容器对事件监听机制的支持

Spring 容器,具体而言是ApplicationContext接口定义的容器提供了一套相对完善的事件发布和监听框架,其遵循了JDK 中的事件监听标准,并使用容器来管理相关组件,使得用户不用关心事件发布和监听的具体细节,降低了开发难度也简化了开发流程。下面看看对于事件监听机制中的各主要角色,Spring 框架中是如何定义的,以及相关的类体系结构:

  • 事件
    Spring 为容器内事件定义了一个抽象类ApplicationEvent,该类继承了JDK 中的事件基类EventObject。因而自定义容器内事件除了需要继承ApplicationEvent之外,还要传入事件源作为构造参数:
public abstract class ApplicationEvent extends EventObject {

    /** use serialVersionUID from Spring 1.2 for interoperability */
    private static final long serialVersionUID = 7099057708183571937L;

    /** System time when the event happened */
    private final long timestamp;


    /**
     * Create a new ApplicationEvent.
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }


    /**
     * Return the system time in milliseconds when the event happened.
     */
    public final long getTimestamp() {
        return this.timestamp;
    }

}
  • 事件监听器
    Spring 定义了一个ApplicationListener接口作为事件监听器的抽象,接口定义如下:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);

}
  1. 该接口继承了 JDK 中表示事件监听器的标记接口EventListener,内部只定义了一个抽象方法onApplicationEvent(evnt),当监听的事件在容器中被发布,该方法将被调用。
  2. 同时,该接口是一个泛型接口,其实现类可以通过传入泛型参数指定该事件监听器要对哪些事件进行监听。这样有什么好处?这样所有的事件监听器就可以由一个事件发布器进行管理,并对所有事件进行统一发布,而具体的事件和事件监听器之间的映射关系,则可以通过反射读取泛型参数类型的方式进行匹配,稍后我们会对原理进行讲解。
  3. 最后,所有的事件监听器都必须向容器注册,容器能够对其进行识别并委托容器内真正的事件发布器进行管理。
  • 事件发布器
    ApplicationContext接口继承了ApplicationEventPublisher接口,从而提供了对外发布事件的能力。
    那么是否可以说ApplicationContext即容器本身就担当了事件发布器的角色呢?其实这是不准确的,容器本身仅仅是对外提供了事件发布的接口,真正的工作其实是委托给了具体容器内部一个ApplicationEventMulticaster对象,其定义在AbstractApplicationContext抽象类内部,如下所示:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {
    ...
    /** Helper class used in event publishing */
    @Nullable
    private ApplicationEventMulticaster applicationEventMulticaster;
    ...
}

所以,真正的事件发布器是ApplicationEventMulticaster,这是一个接口,定义了事件发布器需要具备的基本功能:管理事件监听器以及发布事件。其默认实现类是
SimpleApplicationEventMulticaster,该组件会在容器启动时被自动创建,并以单例的形式存在,管理了所有的事件监听器,并提供针对所有容器内事件的发布功能。

3. 基于 Spring 实现自定义事件与监听

想象我们正在做一个任务调度系统,我们需要把任务提交到集群中并监控任务的执行状态,当任务执行完毕(成功或者失败),除了必须对数据库进行更新外,还可以执行一些额外的工作:比如将任务执行结果以邮件的形式发送给用户。这些额外的工作后期还有较大的变动可能:比如还需要以短信的形式通知用户,对于特定的失败任务需要通知相关运维人员进行排查等等,所以不宜直接写死在主流程代码中。最好的方式自然是以事件监听的方式动态的增删对于任务执行结果的处理逻辑。为此我们可以基于 Spring 实现自定义事件与监听,打造一个能够对任务执行结果进行监听的弹性系统。

  • 自定任务结束事件
    定义一个任务结束事件TaskFinishedEvent,该类继承抽象类ApplicationEvent来遵循容器事件规范。
package com.tongbanjie.application.event;

import org.springframework.context.ApplicationEvent;

/**
 * @author Lu. Yan
 * @create 2019-06-26
 */
public class TaskFinishedEvent extends ApplicationEvent {
    
    /**
     * Create a new ApplicationEvent.
     *
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public TaskFinishedEvent(Object source) {
        super(source);
    }
}
  • 自定义邮件服务监听器并向容器注册
    该类实现了容器事件规范定义的监听器接口ApplicationListener<?>,通过泛型参数指定对上面定义的任务结束事件进行监听,通过 @Component 注解向容器进行注册。
package com.tongbanjie.application.listener;

import com.tongbanjie.application.event.TaskFinishedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @author Lu. Yan
 * @create 2019-06-26
 */
@Component
public class MailTaskFinishedListener implements ApplicationListener<TaskFinishedEvent> {

    private String emailAddr ="xxxxxx@163.com";

    @Override
    public void onApplicationEvent(TaskFinishedEvent event) {

        System.out.println("Send Email to "+ emailAddr +" Task:"+event.getSource());
    }
}
  • 发布事件
    从上面对 Spring 事件监听机制的类结构分析可知,发布事件的功能定义在ApplicationEventPublisher接口中,而ApplicationContext继承了该接口,所以最好的方法是通过实现ApplicationContextAware接口获取ApplicationContext实例,然后调用其发布事件方法。
@Component
public class EventPublisher implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    //发布事件
    public void publishEvent(ApplicationEvent event){

        applicationContext.publishEvent(event);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}

这样就可以在任务结束之后,调用EventPublisher#publishEvent(ApplicationEvent)方法,来发布TaskFinishedEvent事件。

4. Spring 事件监听源码解析

Spring 事件监听机制离不开容器 IOC 特性提供的支持,比如容器会自动创建事件发布器,自动识别用户注册的监听器并进行管理,在特定的事件发布后会找到对应的事件监听器并对其监听方法进行回调。Spring 帮助用户屏蔽了关于事件监听机制背后的很多细节,使用户可以专注于业务层面进行自定义事件开发。然而我们还是忍不住对其背后的实现原理进行一番探讨,比如:

  1. 事件发布器ApplicationEventMulticaster是何时被初始化的,初始化过程中都做了什么?
  2. 注册事件监听器的过程是怎样的,容器怎么识别出它们并进行管理?
  3. 容器发布事件的流程是怎样的?它如何根据发布的事件找到对应的事件监听器,事件和由该事件触发的监听器之间的匹配规则是怎样的?

为了对以上问题进行解答,我们不得不深入源码层面一探究竟。

4.1 初始化事件发布器流程

真正的事件发布器是ApplicationEventMulticaster,它定义在AbstractApplicationContext中,并在ApplicationContext容器启动的时候进行初始化。在容器启动的refrsh()方法中可以找到初始化事件发布器的入口方法,如下图所示:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {
    ...
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

    /**
     * Initialize the ApplicationEventMulticaster.
     * Uses SimpleApplicationEventMulticaster if none defined in the context.
     * @see org.springframework.context.event.SimpleApplicationEventMulticaster
     */
    protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        // 判断beanFactory里是否定义了id为applicationEventMulticaster的bean,默认是没有的
        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 {
            //一般情况会走这里,创建一个SimpleApplicationEventMulticaster并交由容器管理
            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 + "]");
            }
        }
    }

}

initApplicationEventMulticaster()方法会初始化ApplicationEventMulticasterSimpleApplicationEventMulticaster的实例。之后会调用beanFactory.registerSingleton方法将创建的SimpleApplicationEventMulticaster实例注册为容器的单实例 bean。

初始化事件发布器的工作非常简单,一句话总结:由容器实例化用户自定义的事件发布器或者由容器帮我们创建一个简单的事件发布器并交由容器管理。

4.2 注册事件监听器流程

注册事件监听器的流程在初始化事件发布器之后,在registerListeners()方法中:

    protected void registerListeners() {
        // Register statically specified listeners first.
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }

        //获取实现ApplicationListener接口的所有bean的beanNamegetBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            //将监听器的beanName保存到事件发布器中
            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);
            }
        }
    }

    public void addApplicationListener(ApplicationListener<?> listener) {
        synchronized (this.retrievalMutex) {
            // Explicitly remove target for a proxy, if registered already,
            // in order to avoid double invocations of the same listener.
            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.retrievalMutex) {
            this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
            this.retrieverCache.clear();
        }
    }

主要流程为:

  1. 遍历AbstractApplicationContext#applicationListeners的属性,其存放的是事件监听器ApplicationListener集合。将applicationListeners集合存入其关联的事件发布器SimpleApplicationEventMulticaster.ListenerRetriever#applicationListeners属性中。
  2. 遍历beanFactory中所有的 bean,获取所有实现ApplicationListener接口的 bean 的 beanName,并将这些 beanName 注册到其关联的事件发布器SimpleApplicationEventMulticaster.ListenerRetriever#applicationListenerBeans属性中。
  3. 如果有earlyApplicationEvents,则先发布这些事件。

defaultRetriever是定义在抽象类AbstractApplicationEventMulticaster中的成员,用来保存所有事件监听器及其 beanName。

public abstract class AbstractApplicationEventMulticaster
        implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {

    private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
}

private class ListenerRetriever {

        //保存所有事件监听器
        public final Set<ApplicationListener<?>> applicationListeners;

        //保存所有事件监听器的beanName
        public final Set<String> applicationListenerBeans;

其实看到这里会有一个疑问,registerListeners()方法只是找到了所有监听器的 beanName 并将其保存到了事件发布器ApplicationEventMulticaster中,那么真正的事件监听器实例是何时被创建并被加入到事件发布器中的?

这里我们不得不退回到启动容器的refresh()方法中,在初始化 beanFactory 之后,初始化事件发布器之前,容器在prepareBeanFactory(beanFactory)方法中又注册了一些重要组件,其中就包括一个特殊的BeanPostProcessor:ApplicationListenerDetector,正如其类名暗示的那样,这是一个事件监听器的探测器。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Tell the internal bean factory to use the context's class loader etc.
        beanFactory.setBeanClassLoader(getClassLoader());
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
        ...
        // Register early post-processor for detecting inner beans as ApplicationListeners.
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        // Detect a LoadTimeWeaver and prepare for weaving, if found.
        if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            // Set a temporary ClassLoader for type matching.
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

        // Register default environment beans.
        if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }
    }

该类实现了BeanPostProcessor接口,如下图所示:

ApplicationListenerDetector实现了BeanPostProcessor接口,可以在容器级别对所有 bean 的生命周期过程进行增强。这里主要是为了能够在初始化所有 bean 后识别出所有的事件监听器 bean 并将其注册到事件发布器中。

    public Object postProcessAfterInitialization(Object bean, String beanName) {
      //判断该bean是否实现了ApplicationListener接口       
      if (this.applicationContext != null && 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
                //将实现了ApplicationListener接口的bean注册到事件发布器applicationEventMulticaster中
                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;
    }

在初始化所有的 bean 后,该ApplicationListenerDetectorpostProcessAfterInitialization(Object bean, String beanName)方法会被作用在每一个 bean 上,通过判断传入的 bean 是否是ApplicationListener实例进行过滤,然后将找到的事件监听器 bean 注册到事件发布器中。

4.3 容器事件发布流程

发布事件,调用AbstractApplicationContext#publishEvent(ApplicationEvent)方法:

  public void publishEvent(ApplicationEvent event) {
        publishEvent(event, null);
    }

  protected void publishEvent(Object event, 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<Object>(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);
            }
        }
    }

这里为了简化源码阅读的工作量,对一些细节和分支情形做了忽略,只考虑主流程,如上图箭头所示,这里调用了事件发布器的multicastEvent()方法进行事件发布,需要传入事件event和事件类型eventType作为参数。不过通常这个eventType参数为 null,因为事件的类型信息完全可以通过反射的方式从event对象中获得。继续跟进源码:

public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
        //获取事件类型
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        //遍历所有和事件匹配的事件监听器
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                //异步回调监听方法
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        invokeListener(listener, event);
                    }
                });
            }
            else {
                //同步回调监听方法
                invokeListener(listener, event);
            }
        }
    }

首先通过传入的参数或者通过调用resolveDefaultEventType(event)方法获取事件的事件类型信息,之后会通过getApplicationListeners(event, type)方法得到所有和该事件类型匹配的事件监听器,其实现逻辑后面会细说,这里先跳过。对于匹配的每一个监听器,看事件发布器内是否设置了任务执行器实例Executor,决定以何种方式对监听器的监听方法进行回调。

  • 若执行器实例Executor未设置,则进行同步回调,即在当前线程执行监听器的回调方法
  • 若用户设置了Executor实例(通常而言是线程池),则会进行异步回调,监听器的监听方法会交由线程池中的线程去执行。

默认情况下容器不会为用户创建执行器实例,因而对监听器的回调是同步进行的,即所有监听器的监听方法都在推送事件的线程中被执行,通常这也是处理业务逻辑的线程,若其中一个监听器回调执行阻塞,则会阻塞整个业务处理的线程,造成严重的后果。而异步回调的方式,虽然不会导致业务处理线程被阻塞,但是不能共享一些业务线程的上下文资源,比如类加载器,事务等等。因而究竟选择哪种回调方式,要视具体业务场景而定。

好了,现在可以来探究下困扰我们很久的一个问题了,那就是:如何根据事件类型找到匹配的所有事件监听器?这部分逻辑在getApplicationListeners方法中:

protected Collection<ApplicationListener<?>> getApplicationListeners(
            ApplicationEvent event, ResolvableType eventType) {
        //获取事件中的事件源对象
        Object source = event.getSource();
        //获取事件源类型
        Class<?> sourceType = (source != null ? source.getClass() : null);
        //以事件类型和事件源类型为参数构建一个cacheKey,用于从缓存map中获取与之匹配的监听器列表
        ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

        // Quick check for existing entry on ConcurrentHashMap...
        ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
        if (retriever != null) {
            //从缓存中获取监听器列表
            return retriever.getApplicationListeners();
        }

        if (this.beanClassLoader == null ||
                (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
                        (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
            // Fully synchronized building and caching of a ListenerRetriever
            synchronized (this.retrievalMutex) {
                retriever = this.retrieverCache.get(cacheKey);
                if (retriever != null) {
                    return retriever.getApplicationListeners();
                }
                retriever = new ListenerRetriever(true);
                //查找所有与发布事件匹配的监听器列表
                Collection<ApplicationListener<?>> listeners =
                        retrieveApplicationListeners(eventType, sourceType, retriever);
                //将匹配结果缓存到map中
                this.retrieverCache.put(cacheKey, retriever);
                return listeners;
            }
        }
        else {
            // No ListenerRetriever caching -> no synchronization necessary
            return retrieveApplicationListeners(eventType, sourceType, null);
        }
    }

整个流程可以概括为:

  1. 首先从缓存retrieverCache中查找,这个 map 定义在事件发布器的抽象类中AbstractApplicationEventMulticaster:
final Map<ListenerCacheKey, ListenerRetriever> retrieverCache =
            new ConcurrentHashMap<ListenerCacheKey, ListenerRetriever>(64);

ListenerCacheKey由事件类型eventType和事件源类型sourceType构成,ListenerRetriever内部则维护了一个监听器列表。当所发布的事件类型和事件源类型与 Map 中的 key 匹配时,将直接返回 value 中的监听器列表作为匹配结果,通常这发生在事件不是第一次发布时,能避免遍历所有监听器并进行过滤,如果事件时第一次发布,则会执行流程2。

  1. 遍历所有的事件监听器,并根据事件类型和事件源类型进行匹配。这部分逻辑在retrieveApplicationListeners方法中:
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
            ResolvableType eventType, Class<?> sourceType, ListenerRetriever retriever) {
        //这是存放匹配的监听器的列表
        LinkedList<ApplicationListener<?>> allListeners = new LinkedList<ApplicationListener<?>>();
        Set<ApplicationListener<?>> listeners;
        Set<String> listenerBeans;
        synchronized (this.retrievalMutex) {
            listeners = new LinkedHashSet<ApplicationListener<?>>(this.defaultRetriever.applicationListeners);
            listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
        }
        //遍历所有的监听器
        for (ApplicationListener<?> listener : listeners) {
            //判断该事件监听器是否匹配
            if (supportsEvent(listener, eventType, sourceType)) {
                if (retriever != null) {
                    retriever.applicationListeners.add(listener);
                }
                //将匹配的监听器加入列表
                allListeners.add(listener);
            }
        }
        //遍历所有的监听器 BeanName
        if (!listenerBeans.isEmpty()) {
            BeanFactory beanFactory = getBeanFactory();
            for (String listenerBeanName : listenerBeans) {
                try {
                    Class<?> listenerType = beanFactory.getType(listenerBeanName);
                    if (listenerType == null || supportsEvent(listenerType, eventType)) {
                        // 根据监听器 BeanName 获取监听器实例
                        ApplicationListener<?> listener =
                                beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                        if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                            if (retriever != null) {
                                retriever.applicationListenerBeans.add(listenerBeanName);
                            }
                            //将匹配的监听器加入列表
                            allListeners.add(listener);
                        }
                    }
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Singleton listener instance (without backing bean definition) disappeared -
                    // probably in the middle of the destruction phase
                }
            }
        }
        // 对匹配完成的监听器进行排序
        AnnotationAwareOrderComparator.sort(allListeners);
        return allListeners;
    }

判断监听器是否匹配的逻辑在supportsEvent(listener, eventType, sourceType)中:

protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, Class<?> sourceType) {
        //对原始的ApplicationListener进行一层适配器包装成为GenericApplicationListener
        GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
                (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
                //判断监听器是否支持该事件类型以及该事件源类型
        return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
    }

首先对原始的ApplicationListener进行一层适配器包装成GenericApplicationListener,便于后面使用该接口中定义的方法判断监听器是否支持传入的事件类型或事件源类型:


public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {

   /**
    * Determine whether this listener actually supports the given event type.
    */
   boolean supportsEventType(ResolvableType eventType); //判断是否支持该事件类型

   /**
    * Determine whether this listener actually supports the given source type.
    */
   boolean supportsSourceType(Class<?> sourceType);   //判断是否支持该事件源类型

}

smartListener.supportsEventType(eventType)用来判断监听器是否支持该事件类型:

public boolean supportsEventType(ResolvableType eventType) {
        if (this.delegate instanceof SmartApplicationListener) {
            Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
            return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
        }
        else {
            return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
        }
    }

因为我们的监听器实例通常都不是SmartApplicationListener类型,而是GenericApplicationListener,所以直接看 else 语句。
declaredEventType是监听器泛型的实际类型,而eventType是发布的事件的类型
declaredEventType.isAssignableFrom(eventType)当以下两种情况返回 true

  1. declaredEventTypeeventType类型相同
  2. declaredEventTypeeventType的父类型

只要监听器泛型的实际类型和发布的事件类型一样或是它的父类型,则该监听器将被成功匹配。

而对于事件源类型的判断supportsSourceType(Class<?>),由于通常事件监听器为GenericApplicationListener,故通常会直接返回 true。

public boolean supportsSourceType(Class<?> sourceType) {
        return !(this.delegate instanceof SmartApplicationListener) ||
                ((SmartApplicationListener) this.delegate).supportsSourceType(sourceType);
    }

最后,将所有匹配的监听器与其事件类型缓存到retrieverCache这个 map 中。

Collection<ApplicationListener<?>> listeners =
                        retrieveApplicationListeners(eventType, sourceType, retriever);
                //将匹配结果缓存到map中
                this.retrieverCache.put(cacheKey, retriever);
                return listeners;

梳理下容器事件发布的整个流程,可以总结如下:


5. 总结

这篇文章主要是为了梳理下 Spring 的容器内事件体系并对其工作原理做一定程度上的源码上的剖析。Spring Boot 的事件监听机制复用了 Spring 的内建事件体系。但是 Spring 内建事件为 Spring 应用上下文事件,即ApplicationContextEvent,其事件源为ApplicationContext。而 Spring Boot 的事件类型是SpringApplicationEvent,事件源则是SpringApplication。Spring Boot 事件监听手段仅为SpringApplication关联的ApplicationListener集合。其关联途径有二:

  1. SpringApplication构造阶段在 Class Path 下所加载的 META-INF/spring.factories 资源中的ApplicationListener对象集合。
  2. 通过方法SpringApplication#addListeners(ApplicationListener...)SpringApplicationBuilder#listeners(ApplicationListener...)显式装配。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容