偶尔记一下

没事看看 - MyBatis工具

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

上一篇: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 继续。

阅读更多

扫码向博主提问

去开通我的Chat快问

isea533

博客专家

MyBatis相关答疑
  • 擅长领域:
  • MyBatis
  • Spring Boo
  • Spring
版权声明:版权归博主所有,转载请带上本文链接!联系方式:abel533@gmail.com https://blog.csdn.net/isea533/article/details/80194128
个人分类: devtools
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

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

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭