Spring Developer Tools 源码分析:五、事件触发过程

 2019-11-02 21:10  阅读(904)
文章分类:Spring boot

上一篇:Spring Developer Tools 源码分析:四、类加载器

本篇是下一篇 Restarter 的前置内容,这里介绍的 ApplicationListener 事件触发过程是针对整个 Spring Boot 的过程。

Spring Developer Tools 通过 ApplicationListener 不同阶段的事件来控制 Restarter 的运行。Restarter 包含了获取 main 方法类,初始化监控资源路径等功能。

devtools 项目的 "META-INF/spring.factories" 文件中,和这里相关的配置如下:

# Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.devtools.restart.RestartApplicationListener

RestartApplicationListener 类定义如下(使用了最高的优先级):

public class RestartApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered {

接口实现方法如下:

@Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationStartingEvent) {
            onApplicationStartingEvent((ApplicationStartingEvent) event);
        }
        if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent((ApplicationPreparedEvent) event);
        }
        if (event instanceof ApplicationReadyEvent
                || event instanceof ApplicationFailedEvent) {
            Restarter.getInstance().finish();
        }
        if (event instanceof ApplicationFailedEvent) {
            onApplicationFailedEvent((ApplicationFailedEvent) event);
        }
    }

在这里可以看到 RestartApplicationListener 会监控 4 类事件,分别如下:

  • ApplicationStartingEvent 启动时,这个事件会尽可能早的触发。
  • ApplicationPreparedEventApplicationContext 已完全准备但未刷新(refresh) 时发布的事件。bean 的定义将会被加载,并且 Environment 已经可以在这个阶段使用了。
  • ApplicationReadyEvent 已经准备好提供服务。
  • ApplicationFailedEvent 启动失败。

下面用尽可能短的代码和语言来说明这些事件的触发过程。

事件触发过程

SpringApplication 构造方法中,有如下代码:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        //省略其他
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //省略其他
    }

这里会从 "META-INF/spring.factories" 文件夹获取所有 ApplicationListener 类的实现,由于 Spring 还没启动,所以这里只能通过读取资源文件来获取一些启动更早的接口。获取所有的实现接口后,会 set 到当前类的 listeners 中。在这一步,就能获取到 RestartApplicationListener

接下来是 SpringApplicationrun 方法。

public ConfigurableApplicationContext run(String... args) {
        //省略部分
        SpringApplicationRunListeners listeners = getRunListeners(args);//1
        listeners.starting();//ApplicationStartingEvent - ① 
        try {
            //省略
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);//ApplicationEnvironmentPreparedEvent
            //省略
            context = createApplicationContext();
            //省略
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);//ApplicationPreparedEvent - ②
            refreshContext(context);
            //省略
            listeners.started(context);//ApplicationStartedEvent
            //省略
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);//ApplicationFailedEvent - ④
            throw new IllegalStateException(ex);
        }
        try {
            listeners.running(context);//ApplicationReadyEvent - ③
        }
        //省略
        return context;
    }

上面代码只留下了和 listeners 相关的主干代码。你可以发现这里都是和 SpringApplicationRunListeners 有关的代码,和前面提到的 ApplicationListener 不是一个接口,这之间是什么关系呢。
//1 处,通过 getRunListeners 获取的 SpringApplicationRunListeners(代理类),从这个方法来看,这里也是从 spring-boot 项目下的 "META-INF/spring.factories" 获取的所有的 SpringApplicationRunListener 实现类。在这个配置文件中有下面的配置:

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

SpringApplicationRunListeners 中会实际执行所有 runlistener 的方法。在 EventPublishingRunListener 中,会通过 SpringApplication.getListeners() 获取一开始的 listener ,在后续触发事件时会执行。除此之外还要特别注意 EventPublishingRunListener 中的 contextLoaded 方法,代码如下:

@Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        for (ApplicationListener<?> listener : this.application.getListeners()) {
            if (listener instanceof ApplicationContextAware) {
                ((ApplicationContextAware) listener).setApplicationContext(context);
            }
            context.addApplicationListener(listener);
        }
        this.initialMulticaster.multicastEvent(
                new ApplicationPreparedEvent(this.application, this.args, context));
    }

这里有两个特殊处理,首先如果实现了 ApplicationContextAware 接口,就会在此时去调用该方法。还有就是会把 listener 添加到已经创建的 context 中,添加到 context 是非常重要的一个步骤。EventPublishingRunListener 只会在 Spring Boot 启动过程中处理事件,当 run 方法执行完毕后,后续的事件都是在 context 范围内的,这里加入 context 后,就能处理其他感兴趣的事件了。

前面自动配置中介绍的 @EventListener 会在所有单例的 bean 创建完成后,通过 EventListenerMethodProcessor 处理注解形式的事件监听,这些方法也都会通过代理转换为 ApplicationListener,然后加入到 context 中。

具体哪个阶段触发哪个事件,大家可以根据上面的主干代码去细看(代码注释已经标明了会触发的事件),下一篇回到 Restarter 继续。


来源:http://ddrv.cn

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring Developer Tools 源码分析:五、事件触发过程

相关推荐