Spring 事件监听机制源码详解

202 阅读15分钟

Spring 事件发布订阅机制

Spring 提供了许多非常好用的机制,比如IOC,AOP。这些几乎在所有的Spring项目中都有广泛的使用,这里讲解的是Spring提供的事件发布订阅机制,掌握发布订阅设计模式可以更好的在项目中对功能进行设计,也多一种解决方案。同时如果你掌握了SpringBoot的事件发布的全部流程,你就掌握了SpringBoot在整个启动过程中干了什么事,走了哪些流程

使用案例

事件类

public class MyEvent extends ApplicationEvent {
    public MyEvent(Object source) {
        super(source);
    }
}

订阅类

@Component
public class MyEventSubscribe implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent event) {
        String msg = (String) event.getSource();
        System.out.println("我接受到了事件,msg: "+msg);
    }
}

发布类

@Component
public class MyTest implements CommandLineRunner {
    @Autowired
    ApplicationEventPublisher applicationEventPublisher;
​
    @Override
    public void run(String... args) throws Exception {
        applicationEventPublisher.publishEvent(new MyEvent("hello event"));
​
    }
}

Log输出

2024-09-25 14:44:23.852  INFO 22492 --- [           main] c.c.s.SpringResCode1Application          : Starting SpringResCode1Application using Java 1.8.0_202 on TCN1214966 with PID 22492 (D:\code\java\springResCode1\target\classes started by changtao.deng in D:\code\java\springResCode1)
2024-09-25 14:44:23.858  INFO 22492 --- [           main] c.c.s.SpringResCode1Application          : No active profile set, falling back to 1 default profile: "default"
我接受到了事件,msg: hello event
2024-09-25 14:44:24.555  INFO 22492 --- [           main] c.c.s.SpringResCode1Application          : Started SpringResCode1Application in 1.243 seconds (JVM running for 2.426)
​

除了自定义事件以外,你还可以监听SpringBoot 应用在启动过程中的发布的事件

@Component
public class MyEventSubscribe {
​
    @EventListener
    public void handleMyEvent1(ApplicationStartingEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ApplicationStartingEvent事件,msg: " + source);
    }
​
    @EventListener
    public void handleMyEvent2(ApplicationEnvironmentPreparedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ApplicationEnvironmentPreparedEvent事件,msg: " + source);
    }
​
    @EventListener
    public void handleMyEvent3(ApplicationContextInitializedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ApplicationContextInitializedEvent事件,msg: " + source);
    }
​
    @EventListener
    public void handleMyEvent4(ApplicationPreparedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ApplicationPreparedEvent事件,msg: " + source);
    }
​
    @EventListener
    public void handleMyEvent6(ContextRefreshedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ContextRefreshed事件,msg: " + source);
    }
    
    @EventListener
    public void handleMyEvent5(ApplicationStartedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ApplicationStartedEvent事件,msg: " + source);
    }
​
    @EventListener
    public void handleMyEvent7(ContextClosedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ContextClosed事件,msg: " + source);
    }
​
    @EventListener
    public void handleMyEvent8(ContextStoppedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ContextStopped事件,msg: " + source);
    }
}

我这里还不是完全列举就写了八个事件,还是比较多的,那让我们执行下

2024-09-25 15:20:39.298  INFO 12724 --- [           main] c.c.s.SpringResCode1Application          : No active profile set, falling back to 1 default profile: "default"
我接受到了ContextRefreshed事件,msg: org.springframework.context.annotation.AnnotationConfigApplicationContext@14cd1699, started on Wed Sep 25 15:20:39 CST 2024
2024-09-25 15:20:40.093  INFO 12724 --- [           main] c.c.s.SpringResCode1Application          : Started SpringResCode1Application in 1.272 seconds (JVM running for 2.278)
我接受到了ApplicationStartedEvent事件,msg: org.springframework.boot.SpringApplication@5f0e9815
我接受到了ContextClosed事件,msg: org.springframework.context.annotation.AnnotationConfigApplicationContext@14cd1699, started on Wed Sep 25 15:20:39 CST 2024
​
Process finished with exit code 0

并没有全部触发,只触发了ContextRefreshed,ApplicationStarted 以及最后的ContextClosed。

这里就先简单讲下这些事件

  1. ApplicationStartingEvent

    • 在运行开始时发送,但在任何处理开始之前。此时,监听器和初始化器还未被注册。
  2. ApplicationEnvironmentPreparedEvent

    • 在环境准备好后发送,但在创建 ApplicationContext 之前。这时,Environment 已经准备好,可以用于配置和处理。
  3. ApplicationContextInitializedEvent

    • ApplicationContext 初始化完成后发送,但在刷新之前。此时,所有的 ApplicationContextInitializer 已经被调用。
  4. ApplicationPreparedEvent

    • ApplicationContext 准备完成后发送,但在刷新之前。此时,所有的 Bean 定义已经加载,但尚未实例化。
  5. ApplicationStartedEvent

    • ApplicationContext 刷新并启动完成后发送。这标志着应用程序已经完全启动并准备好处理请求。
  6. ContextRefreshedEvent

    • ApplicationContext 完成刷新时发送。此时,所有的单例 Bean 已经被实例化并且已完成初始化。
  7. ContextStoppedEvent

    • ApplicationContext 停止时发送。此事件需要显式调用 stop() 方法。
  8. ContextClosedEvent

    • ApplicationContext 关闭时发送。这通常在 JVM 关闭或显式调用 close() 方法时发生。

因为我们的Bean是通过@Component注解来进行IOC注入的,所以上下文没有完成所有的Bean注入前的事件这个监听器是监听不到的,也就是ApplicationStartedEvent 之前的事件无法监听到。那有没有办法监听更前面的事件呢,其实也有,那就是通过SPI的方式进行注入,因为SPI的注入会在SpringContext的构造方法中就进行执行。

事件监听机制源码

通过SpringApplication.run(SpringResCode1Application.class, args); 来进行应用启动,点击SpringApplication方法查看后找到第一行出现Listener的地方

 public ConfigurableApplicationContext run(String... args) {
        long startTime = System.nanoTime();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        // 这里获取监听器
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        // 发布了一个事件
        listeners.starting(bootstrapContext, this.mainApplicationClass);

getRunListeners 方法

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
    return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
}

return new SpringApplicationRunListeners传递了三个参数,第一个是日志,第三个是当前的启动类,第二个参数是用过getSpringFactoriesInstances方法获取的一个集合,如果熟悉SpringBoot源码的应该知道 SPI机制,这也是Boot能够自动装配的原理,这里的getSpringFactoriesInstances 就是通过SPI机制获取 SpringApplicationRunListener 接口的实现类。

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

可以看到就配置了一个实现类,而这个实现类就是Springboot完成事件订阅发布的核心

SpringApplicationRunListeners 构造方法,中间的listeners就是被SPI实例化的实现类

SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners, ApplicationStartup applicationStartup) {
    this.log = log;
    this.listeners = new ArrayList(listeners);
    this.applicationStartup = applicationStartup;
}

简单看看 SpringApplicationRunListeners 类内部是如何进行事件发布的

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
        this.doWithListeners("spring.boot.application.starting", (listener) -> {
            listener.starting(bootstrapContext);
        }, (step) -> {
            if (mainApplicationClass != null) {
                step.tag("mainApplicationClass", mainApplicationClass.getName());
            }
​
        });
    }
​
    void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        this.doWithListeners("spring.boot.application.environment-prepared", (listener) -> {
            listener.environmentPrepared(bootstrapContext, environment);
        });
    }
​
    void contextPrepared(ConfigurableApplicationContext context) {
        this.doWithListeners("spring.boot.application.context-prepared", (listener) -> {
            listener.contextPrepared(context);
        });
    }

调用的都是doWithListeners 这个方法,第一个参数是字符串,第二个参数是一个函数接口,第三个参数是可以执行额外的一个步骤

  private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
        this.doWithListeners(stepName, listenerAction, (Consumer)null);
    }
​
    private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) {
        StartupStep step = this.applicationStartup.start(stepName);
        this.listeners.forEach(listenerAction);
        if (stepAction != null) {
            stepAction.accept(step);
        }
​
        step.end();
    }

这里做一下总结,SpringApplicationRunListeners 类 对于SpringApplicaton来说是一个观察者,被观察者会调用观察者的方法,这样就可以执行一些观察逻辑,这也是最简单的观察者设计模式,可标题不是说Spring的事件订阅发布模式吗,那么接下来就是核心内容讲解

EventPublishingRunListener 源码

唯一一个SpringApplicationRunListener (运行监听器)接口实现类, 虽然名字叫做运行监听器,并不代表他只有在springBoot启动过程中才起作用。

构造方法

public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    Iterator var3 = application.getListeners().iterator();
​
    while(var3.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var3.next();
        this.initialMulticaster.addApplicationListener(listener);
    }
​
}

看到这里的监听器集合的来源是application.getListeners() 而这个接口就是正是ApplicationListener类,也就是整个IOC容器中所有的监听类或者叫做订阅类。

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered

这里就举几个方法看下

public void starting(ConfigurableBootstrapContext bootstrapContext) {
    this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
​
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
    this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
​
public void contextPrepared(ConfigurableApplicationContext context) {
    this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}

可以发现发布的事件都是调用initialMulticaster属性的方法来完成的,所以后面需要关注一下这个属性时如何完成监听器触发事件的

EventPublishingRunListener 的内部属性,也就是下面三个,其中前面两个也不太需要讲解,就是直接通过构造方法进行引用赋值,因为事件就是围绕SpringApplication来执行一些逻辑自然需要获取SpringApplication的引用以及参数

第三个属性需要着重讲解下

private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;

这个类的主要作用也就是实现事件发布功能

SimpleApplicationEventMulticaster(事件广播器实现类) 源码
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
​
    @Nullable
    private Executor taskExecutor;
​
    @Nullable
    private ErrorHandler errorHandler;
​
    @Nullable
    private volatile Log lazyLogger;
}

SimpleApplicationEventMulticaster 属性中并没有实际想象的复杂,一个线程池,一个错误处理器,以及一个懒加载的日志,拿主要的事件广播都写在哪里呢,翻看方法

@Override
    public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
        Executor executor = getTaskExecutor();
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null && listener.supportsAsyncExecution()) {
                try {
                    executor.execute(() -> invokeListener(listener, event));
                }
                catch (RejectedExecutionException ex) {
                    // Probably on shutdown -> invoke listener locally instead
                    invokeListener(listener, event);
                }
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

他通过getApplicationListeners方法来获取监听器集合,然后执行invokeListener方法(如果配了异步执行则用线程池进行调用) 最后会执行到doInvokeListener

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
       listener.onApplicationEvent(event);
    }

这里直接调用listener方法。所以整体来说还是不难的。

事件调度中心

回到刚刚的multicastEvent 方法 在for循环中调用的getApplicationListeners方法其实是父类提供的方法,所以我们得先去看看它的父类关系。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

image-20240911161130391.png

Aware接口这里不作解析,作用就是获取springboot相关的环境,这里主要讲解ApplicationEventMulticaster 这个接口

ApplicationEventMulticaster 事件广播器接口
public interface ApplicationEventMulticaster {
    // 添加一个事件监听器,通过类
    void addApplicationListener(ApplicationListener<?> listener);
    // 添加一个事件监听器,通过beanName
    void addApplicationListenerBean(String listenerBeanName);
    // 移除一个事件监听器,通过类
    void removeApplicationListener(ApplicationListener<?> listener);
    // 移除一个事件监听器,通过beanName
    void removeApplicationListenerBean(String listenerBeanName);
    // 移除事件监听器,通过断言类型
    void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate);
    // 移除事件监听器,通过断言beanName
    void removeApplicationListenerBeans(Predicate<String> predicate);
    // 移除所有事件监听器
    void removeAllListeners();
    // 对所有监听器进行事件发布
    void multicastEvent(ApplicationEvent event);
    // 指定泛型类的事件发布,只会发布到匹配泛型的监听器上
    void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}

虽然看起来接口比较多,但其实仔细分析后就干了三件事,添加,发布以及删除。这个接口的实现类其实就是事件调度中心,。

这里你可能会关注到最后一个方法,这个方法也是在SimpleApplicationEventMulticaster 类中调用的执行方法,用于执行与事件相匹配的监听器的监听方法。传递了两个参数,一个是event,一个是eventType。这两个参数其实从名字就可以看出,第一个是事件本身,第二个则是这个事件的类型。这两个对象我们可以对之前的demo进行debug查看下内容是啥

image-20240913103719195.png

event非常简单,一个就是时间戳,第二个就是源事件,也就是一个字符串

image-20240913103810361.png

eventType其实就是解析了这个事件的全类名是什么,这里就是MyEvent类型,然后事件广播器就会寻找与这个事件类型匹配的监听器然后执行相应的监听方法。

分析完接口后再分析AbstractApplicationEventMulticaster实现类也就简单很多了

实现类 AbstractApplicationEventMulticaster(抽象应用事件广播器)源码解析

事件广播器作为监听器的容器需要有添加,删除功能。然后还有每次外部调用触发方法后事件广播器会触发监听器的事件方法,如果不太理解的可以看下发布订阅的设计模式,这里基本一样,但是其内部又做了一些优化处理(主要是缓存),如果想知道详细内容可以继续往下看

内部属性

public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
    // 默认检索器,其实只有存储的功能
    private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();
    // 缓存Map
    final Map<ListenerCacheKey, CachedListenerRetriever> retrieverCache = new ConcurrentHashMap(64);
    @Nullable
    private ClassLoader beanClassLoader;
    @Nullable
    private ConfigurableBeanFactory beanFactory;
}

我们重点关注前两个属性第一个DefaultListenerRetriever(默认监听器检索者),第二个CachedListenerRetriever(缓存监听器检索者),后面两个属性是通过aware接口进行直接赋值的这里不做讲解

第一个属性DefaultListenerRetriever(监听器检索者)类AbstractApplicationEventMulticaster的内部类 该属性的主要功能就是用于装载listener实例的。每次AbstractApplicationEventMulticaster调用的addApplicationListener 方法 就是将监听器装进DefaultListenerRetriever集合中的。

第二属性是一个Map类型,key为ListenerCacheKey类型,是AbstractApplicationEventMulticaster类的内部类,value是CachedListenerRetriever类型,也是一个内部类

内部类 ListenerCacheKey

ListenerCacheKey类型比较简单,在后续也会被用到,所有这里先做讲解

private static final class ListenerCacheKey implements Comparable<ListenerCacheKey> {
    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;
        } else if (!(other instanceof ListenerCacheKey)) {
            return false;
        } else {
            ListenerCacheKey otherKey = (ListenerCacheKey)other;
            return this.eventType.equals(otherKey.eventType) && ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType);
        }
    }
    xxxx...

内部持有了两个属性,这两个属性其实是对同一个事件的描述,关于事件和源事件demo那里也做了讲解,这里的key其实就是这个事件类型和源事件类型的复合key,只有当事件类型和源事件类型全部一致时才会认为相同(重写了equals方法)。spring这么做更加精确的缓存匹配。如果没有缓存,那么每次发布事件时就需要遍历所有listener然后分别进行事件匹配,但是如果有了缓存则直接将 CachedListenerRetriever 类中的listener集合拿出来直接发布事件就行

内部类 DefaultListenerRetriever

DefaultListenerRetriever 默认监听器检索者

private class DefaultListenerRetriever {
    public final Set<ApplicationListener<?>> applicationListeners;
    public final Set<String> applicationListenerBeans;
​
    private DefaultListenerRetriever() {
        this.applicationListeners = new LinkedHashSet();
        this.applicationListenerBeans = new LinkedHashSet();
    }

从属性中可以看到他是持有所有监听器的集合类,虽然名字叫做检索者(Retriever ) 但是我翻看源码后发现他并没有方法来实现对监听器的检索,检索的方法是直接在 AbstractApplicationEventMulticaster 类的 getApplicationListeners 方法中的。

内部类 CachedListenerRetriever
private class CachedListenerRetriever {
​
    @Nullable
    public volatile Set<ApplicationListener<?>> applicationListeners;
​
    @Nullable
    public volatile Set<String> applicationListenerBeans;
​
    @Nullable
    public Collection<ApplicationListener<?>> getApplicationListeners() {
       Set<ApplicationListener<?>> applicationListeners = this.applicationListeners;
       Set<String> applicationListenerBeans = this.applicationListenerBeans;
        xxx...
       return allListeners;
    }
}

从名字就看出来是一个缓存检索器,内部有两个缓存集合,在调用getApplicationListeners方法是会通过applicationListenerBeans集合 的 beanName 从beanFactory 获取bean并且加上applicationListeners一起返回。

监听器检索过程

getApplicationListeners(ApplicationEvent event, ResolvableType eventType) 详解

protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
    // 获取事件的源事件
    Object source = event.getSource();
    // 获取源事件类型
    Class<?> sourceType = source != null ? source.getClass() : null;
    // 构造该时间的cacheKey
    ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
    CachedListenerRetriever newRetriever = null;
    // 尝试通过构造的cacheKey从缓存Map中拿到已有的缓存检索者。
    CachedListenerRetriever existingRetriever = (CachedListenerRetriever)this.retrieverCache.get(cacheKey);
    // 如果没有缓存检索者同时beanClassLoader 和 sourceType 是可被安全缓存的(为null也认为是安全的)
    if (existingRetriever == null && (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
        // 构建新的检索者
        newRetriever = new CachedListenerRetriever();
        // putIfAbsent: 如果key已存在则返回改key的value(入宫value == null 也是返回null),如果不存在则返回null并插入值
        // 如果已经存在检索器了,那么后续不再需要初始化缓存,将newRetriever置为null,
        existingRetriever = (CachedListenerRetriever)this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
        if (existingRetriever != null) {
            newRetriever = null;
        }
    }
    
    if (existingRetriever != null) {
        Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
        // 如果缓存的监听器集合已经有值则直接返回缓存的结果
        if (result != null) {
            return result;
        }
    }
    // 如果缓存没有结果,则需要进行检索监听器
    return this.retrieveApplicationListeners(eventType, sourceType, newRetriever);
}

retrieveApplicationListeners(eventType, sourceType, newRetriever); 源码讲解,内容比较多

retrieveApplicationListeners 方法共有三个参数,前两个(源)事件类型,第三个参数则是CachedListenerRetriever,这里不要忘记上个方法中的逻辑,当newRetriever 为null时其实是当前缓存已有eventType, sourceType 描述cacheKey的value了,也就是说这个事件在过去调用中已经被缓存了,后续是不需要再重复缓存的,方法中多次出现的if (retriever != null) 判断就是判断是否需要缓存,如果true 则需要缓存。

retrieveApplicationListeners 整个方法其实就是遍历DefaultListenerRetriever的属性applicationListeners, applicationListenerBeans 然后判断listener是不是与当前event类型匹配,同时如果之前没有缓存则设置匹配的listener加入缓存

private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {
    List<ApplicationListener<?>> allListeners = new ArrayList();
    // 如果已经有缓存了,那么置为null
    Set<ApplicationListener<?>> filteredListeners = retriever != null ? new LinkedHashSet() : null;
    // 如果已经有缓存了,那么置为null
    Set<String> filteredListenerBeans = retriever != null ? new LinkedHashSet() : null;
    LinkedHashSet listeners;
    LinkedHashSet listenerBeans;
    // 同步块,防止并发
    synchronized(this.defaultRetriever) {
        
        listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
        listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
    }
        // 循环遍历已有的监听器,当监听器的监听事件与事件类型和源事件类型一致时,则加入缓存集合中
        for (ApplicationListener<?> listener : listeners) {
            if (supportsEvent(listener, eventType, sourceType)) {
                if (retriever != null) {
                    filteredListeners.add(listener);
                }
                allListeners.add(listener);
            }
        }
​
        // 这个循环的作用是通过beanName来获取listener 同时加入allListeners集合的
        if (!listenerBeans.isEmpty()) {
            // 通过beanName 来获取bean,需要注意这里通过beanFactory获取的可能是Spring的代理对象
            ConfigurableBeanFactory beanFactory = getBeanFactory();
            for (String listenerBeanName : listenerBeans) {
                try {
                    if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
                        ApplicationListener<?> listener =
                                beanFactory.getBean(listenerBeanName, ApplicationListener.class);
​
                        // 这里调用getSingletonTarget 方法目的是获取原对象,也就是未被AOP包装的对象
                        ApplicationListener<?> unwrappedListener =
                                (ApplicationListener<?>) AopProxyUtils.getSingletonTarget(listener);
                        // 如果这两个对象不一致,那需要剔除源对象,因为这会导致原对象和包装对象都被加入进set集合中(filteredListeners,allListeners)在后续触发方法中同一个事件的监听器会重复触发两次
                     
                        if (listener != unwrappedListener) {
                            // 这里需要对缓存集合filteredListeners 以及全部集合allListeners都做处理
                            if (filteredListeners 以及全部集合 != null && filteredListeners.contains(unwrappedListener)) {
                                filteredListeners.remove(unwrappedListener);
                                filteredListeners.add(listener);
                            }
                            if (allListeners.contains(unwrappedListener)) {
                                allListeners.remove(unwrappedListener);
                                allListeners.add(listener);
                            }
                        }
                        // 如果不是包装对象并且之前allListeners没有添加过该事件事件类型是匹配的 
​
                        if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                            if (retriever != null) {
                                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) {
                    // Singleton listener instance (without backing bean definition) disappeared -
                    // probably in the middle of the destruction phase
                }
            }
        }
        // 根据order接口继续从小到大排序
        AnnotationAwareOrderComparator.sort(allListeners);
        // 这里对缓存检索者的属性进行赋值,这样下一次在前面检索时就可以直接返回listener了
        if (retriever != null) {
            if (CollectionUtils.isEmpty(filteredListenerBeans)) {
                retriever.applicationListeners = new LinkedHashSet<>(allListeners);
                retriever.applicationListenerBeans = filteredListenerBeans;
            }
            else {
                retriever.applicationListeners = filteredListeners;
                retriever.applicationListenerBeans = filteredListenerBeans;
            }
        }
        return allListeners;
}
监听器又是从哪里来的

总结了事件调度中心(广播器)那么springboot是什么时候执行addApplicationListener 添加监听器,以及添加监听器又是从哪里来的呢?

直接在持有SimpleApplicationEventMulticaster属性的类EventPublishingRunListener中搜索addApplicationListener方法后可以发现早在EventPublishingRunListener的构造方法中就对listener进行了添加

public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        Iterator var3 = application.getListeners().iterator();
        // 循环遍历添加
        while(var3.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var3.next();
            this.initialMulticaster.addApplicationListener(listener);
        }
​
    }

那listener是从哪里来的呢,上面代码可以看到他是直接从application.getListeners() 获取,那就对这个方法进行追根溯源后发现还是在SpringApplication的构造方法中进行了赋值

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    ... this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 这里进行了赋值,同样也是通过SPI机制获取实现类
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

最后

这里大概就把整个的事件发布订阅讲解完毕了,整体设计相比普通的发布订阅模式,Spring设计了一套缓存机制,同时处理了代理类重复触发的问题。虽然调用过程是有一些绕,但主要是boot对framework的evet类进行了一层包装调用。