SpringBoot启动流程事件源码解析

2020-03-12  本文已影响0人  每天进步一丢儿丢儿

Spring启动流程

启动 => 环境准备阶段(ConfigurableEnvironment) => 上下文准备阶段(ConfigurableApplicationContext) => 上下文加载阶段(ConfigurableApplicationContext) => 启动完成 => 程序运行。

public interface SpringApplicationRunListener {
    default void starting() {
    }

    default void environmentPrepared(ConfigurableEnvironment environment) {
    }

    default void contextPrepared(ConfigurableApplicationContext context) {
    }

    default void contextLoaded(ConfigurableApplicationContext context) {
    }

    default void started(ConfigurableApplicationContext context) {
    }

    default void running(ConfigurableApplicationContext context) {
    }

    default void failed(ConfigurableApplicationContext context, Throwable exception) {
    }

}

该接口的唯一实现类是:EventPublishingRunListener,其主要作用是用来发布启动过程中对应的各种事件。

SpringBoot启动流程事件发布

1. SpringBoot启动流程与启动事件的对应关系
2. SpringBoot启动事件发布的两种方式

那么为什么启动过程中需要两种不同的事件发布机制呢?那是因为在ApplicationStartedEvent事件以前ApplicationContext还没有准备完成,无法通过ApplicationContext的publishEvent()方法发布启动事件,所以要通过SimpleApplicationEventMulticaster发布事件,具体实现如下:

    @Override
    public void multicastEvent(ApplicationEvent event) {
        multicastEvent(event, resolveDefaultEventType(event));
    }

    @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);
            }
        }
    }
3. SpringBoot启动流程的细节展示

首先基于源码来看一下SpringApplication调用run()方法后做了那些事情

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        // 1.  设置java程序运行的服务器环境 、Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标
        configureHeadlessProperty();
       // 2.   获取Spring运行监听器
        SpringApplicationRunListeners listeners = getRunListeners(args);
       // 3.   发布SpringBoot启动ApplicationStartingEvent事件
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 4.   SpringBoot启动环境准备阶段,发布ApplicationEnvironmentPreparedEvent事件
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
     // 5.   创建ApplicationContext
            context = createApplicationContext();
    //  6.   生成ApplicationContext创建失败时的错误报告
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
     // 7.     ApplicationContext准备阶段,发布ApplicationContextInitializedEvent事件
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
    //8.     到此SpringBoot的ApplicationContext准备完毕,程序启动。发布ApplicationStartedEvent事件
            listeners.started(context);
                        
    //        此处的作用是对所有的Runner接口的实现进行调用
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
 //9.     发布ApplicationFailedEvent事件,启动失败时会报告失败信息
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
   //10.     发布ApplicationReadyEvent事件,监听程序运行事件,运行报错时提交错误报告
            listeners.running(context);
        }
        catch (Throwable ex) {
  //11.     发布ApplicationFailedEvent事件,运行异常时会报告异常信息
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

以上代码中的注释就是SpringBoot启动过程中各个事件的具体发布情况,其中ApplicationPreparedEvent事件在注释7下面的prepareContext方法的最后两句代码中体现。


image.png
4. SpringBoot启动事件的使用
public class MyApplicationEnvironmentPreparedEventListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {


    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        System.out.println("MyApplicationEnvironmentPreparedEventListener: SpringBoot准备应用环境...");
    }
}
public class CustomSpringApplication {

    public static ConfigurableApplicationContext run(Class source,String... args) {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(source);
        List<LauncherService> launcherList = new ArrayList<>();
        ServiceLoader.load(LauncherService.class).forEach(launcherList::add);
        launcherList.forEach(LauncherService::launcher);
        // 添加SpringBoot启动事件监听器
        builder.listeners(new MyApplicationStartingEventListener(),
                new MyApplicationEnvironmentPreparedEventListener(),
                new MyApplicationContextInitializedEventListener(),
                new MyApplicationPreparedEventListener());
        return builder.run(args);
    }

}

ApplicationStartedEvent、ApplicationReadyEvent这两个事件发布的时候,SpringBoot运行环境和应用的上下文已经完全准备完毕,因此我们只需要在自定义的事件监听器上添加@Component注解,容器就会自动管理这些监听器。

@Component
public class MyApplicationStartedEventListener implements ApplicationListener<ApplicationStartedEvent> {

    private final ServletApplication.ApplicationConfig config;

    public MyApplicationStartedEventListener(ServletApplication.ApplicationConfig config) {
        this.config = config;
    }

    @Override
   public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
        System.out.println("MyApplicationStartedEventListener: SpringBoot的应用上下文载入完毕...");
        config.app();
        config.annotations();
    }
}
上一篇下一篇

猜你喜欢

热点阅读