Springboot事件监听机制详解

在SpringBoot启动的整个过程中,有一个很重要的机制,叫事件监听机制。
这个机制如他的名字,在启动的各个阶段,SpringBoot会发布一些事件,而对应的监听器监听到事件之后会做相应的处理。
监听器机制的优点是,当一个事件发布后,会有不同的监听器来处理,如果用户想在这个事件发生时做其他处理,就可以添加一个新的监听器监听这个事件,就有了很强的扩展性。
那么接下来我们就来了解一下SpringBoot监听事件的机制和流程,还有SpringBoot启动过程中所有的事件类型,和事件发布后哪些监听器做了什么处理。

1. 获取SpringFactory中的Listener

众所周知,SpringBoot启动时需要调用SpringApplication.run方法,或者自己new出来一个SpringApplication对象之后调用run方法。
和之前的ApplicationContextInitializer相同,SpringBoot所需要的SpringFactory中所有的ApplicationListener也是在new SpringApplication对象时加载进来的。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

上述代码中倒数第二行,setListeners方法,就是将所有配置在spring.factories文件中,key值为 org.springframework.context.ApplicationListener的类实例化后,设置到SpringApplication对象的listeners属性当中。
没有任何添加的情况下,会有11个Listener被实例化,分别从不同的spring依赖中获取:

  • spring-boot-autoconfigure
    org.springframework.boot.autoconfigure.BackgroundPreinitializer
  • spring-boot
    org.springframework.boot.ClearCachesApplicationListener
    org.springframework.boot.builder.ParentContextCloserApplicationListener
    org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor
    org.springframework.boot.context.FileEncodingApplicationListener
    org.springframework.boot.context.config.AnsiOutputApplicationListener
    org.springframework.boot.context.config.ConfigFileApplicationListener
    org.springframework.boot.context.config.DelegatingApplicationListener
    org.springframework.boot.context.logging.ClasspathLoggingApplicationListener
    org.springframework.boot.context.logging.LoggingApplicationListener
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

以上的Listeners是真正监听到事件之后会去处理逻辑的监听器,但是想要发布事件并且让正确的监听器监听到对应事件,SpringApplicationRunListener是不可或缺的。

2. 获取SpringApplicationRunListener

在SpringApplication的核心,run方法之中,有一个getRunListeners方法

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    ......

进入getRunListeners方法:

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

可以看到也是同样从SpringFactory获取到所有的以org.springframework.boot.SpringApplicationRunListener为key值的RunListener,直接放进了new出来的SpringApplicationRunListeners对象之中。
默认情况下,SpringBoot只有一个ApplicationRunListener,就是在spring-boot依赖中sping.factories定义的org.springframework.boot.context.event.EventPublishingRunListener

3. 发布事件

在SpringBoot启动过程中会在固定的阶段发布不同的时间,比如Starting事件,PrepareEnv事件,Started事件等等。这里我们用Starting事件举个例子:
在上面getRunListeners方法的下面一行 listeners.starting(); 这里就是进入了SpringApplicationRunListeners中对应的方法。

void starting() {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.starting();
    }
}

在方法中我们可以看到,SpringApplicationRunListeners会遍历对象当中所有的RunListener,调用他们的starting方法。而在上一节中,我们已经知道默认情况下SpringBoot只会加载一个RunListener,就是org.springframework.boot.context.event.EventPublishingRunListener。所以循环遍历就只有一个类,我们进入EventPublishingRunListener的starting方法看一下。

@Override
public void starting() {
    this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

这里有一个this.initialMulticaster,这是SpringBoot整个事件监听机制的核心之一,事件广播器,他是在什么时候被初始化的呢?我们看下面的code:

public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    for (ApplicationListener<?> listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener);
    }
}

这是EventPublishingRunListener的构造方法,我们已经知道在getRunListeners方法中,EventPublishingRunListener被初始化成实例对象,在这个时候可以看到一个名为SimpleApplicationEventMulticaster的类被new出来赋给了EventPublishingRunListener的initialMulticaster属性。

我们再回到starting()方法当中,SimpleApplicationEventMulticaster的multicastEvent方法被调用,发布的事件是一个new出来的ApplicationStartingEvent事件。

接下来我们看一下发布一个事件的具体逻辑。

4. SimpleApplicationEventMulticaster的multicastEvent方法

核心逻辑如下:

@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);
        }
    }
}

在这个方法中,有一个getApplicationListeners方法,这个方法是广播事件最核心的一个逻辑。我们已经知道在最开始的时候,SpringApplication通过SpringFactoryLoader将11个ApplicationListener加载了进来,这个方法他的作用就是找到这11中所有关注了这个事件的监听器,之后就能遍历这些监听器去调用监听器里面的逻辑。

这里面的具体逻辑比较复杂,我在这里就不把源码贴出来了,感兴趣的同学可以自行阅读。我在这里用两张图总结一下其中的逻辑。

image.png
image.png

其中的核心在supportEventType这个方法当中,如图所示有两种情况:

  1. 当前ApplicationListener实现了SmartApplicationListener接口
    1.1 判断supportEventType方法,如果支持该event,则进行下一步判断,否则直接返回false
    1.2 如果supportEventType支持该event,继续判断supportSourceType,即判断是否支持当前入口函数对象
  2. 当前ApplicationListener未实现SmartApplicationListener接口,只需判断supportEventType,如果返回true,则该监听器支持当前event,否则不支持。

这里有个小问题,这些ApplicationListener为什么会继承不同的接口,他们之间有什么区别呢?

  • 一般SpringBoot中的listener想要监听一个事件,会继承ApplicationListener接口,需要指定一个event的泛型,实现onApplicationEvent。泛型是为了指定这个监听器关注那个event事件,onApplicationEvent就是当监听到事件后的处理逻辑。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);
}
  • 如果在一个listener内想要监听多个事件,则需要继承SmartApplicationListener接口。SmartApplicationListener继承ApplicationListener并关注所有类型为ApplicationEvent的事件,所有使用他的时候必须实现supportEventType方法,去指定某几个event需要被你实现SmartApplicationListener关注。
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    /**
     * Determine whether this listener actually supports the given event type.
     * @param eventType the event type (never {@code null})
     */
    boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
    /**
     * Determine whether this listener actually supports the given source type.
     * <p>The default implementation always returns {@code true}.
     * @param sourceType the source type, or {@code null} if no source
     */
    default boolean supportsSourceType(@Nullable Class<?> sourceType) {
        return true;
    }
    /**
     * Determine this listener's order in a set of listeners for the same event.
     * <p>The default implementation returns {@link #LOWEST_PRECEDENCE}.
     */
    @Override
    default int getOrder() {
        return LOWEST_PRECEDENCE;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容