Spring Boot 回顾(六):揭秘Spring Boot的启动原理

561 阅读2分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

前言

之前通过一个简短的系列更文,让我们认识了Spring Boot的自动装配,也实现了自己的starter,可以说对Spring Boot也有了一定了解。等等,你真的了解Spring Boot了么,那你知道Spring Boot是什么启动的吗?这。。。。不是点击run就启动了吗?

image.png
下面我们真正认识下Spring Boot的启动过程吧,顺便放下之前系列更文的链接,没看的小伙伴可以看看

run方法详解

首先看下之前项目中的启动类 image.png 我们注意到其中的main方法,里面都有这样一段代码

SpringApplication.run(ClientApplication.class, args);

基本上可以猜测出Spring Boot项目的启动肯定是从这里开始的,那么我们就沿着这里看下去吧。我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return new SpringApplication(sources).run(args);
}

同时,在创建SpringApplication对象实例时,会调用它自己的initialize方法

 private void initialize(Object[] sources) {
        if (sources != null && sources.length > 0) {
            this.sources.addAll(Arrays.asList(sources));
        }
        this.webEnvironment = deduceWebEnvironment();
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

初始化initialize方法执行完之后,开始进入Spring BooT启动流程的重点,这时开始调用我们的run方法,开始启动SpringBoot。源码如下

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
    ConfigurableApplicationContext context = null;
    this.configureHeadlessProperty();
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        Banner printedBanner = this.printBanner(environment);
        context = this.createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        this.refreshContext(context);
        this.afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }

        listeners.started(context);
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        this.handleRunFailure(context, var10, listeners);
        throw new IllegalStateException(var10);
    }

    try {
        listeners.running(context);
        return context;
    } catch (Throwable var9) {
        this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}

整个方法还是比较长的,我们耐心看下去。首先开启我们的StopWatch定时器,用来记录任务的执行时间;接下来调用createBootstrapContext,它主要是初始化启动上下文;接着configureHeadlessProperty进行系统参数的设置;然后是初始化监听器列表,调用getRunListeners;监听器列表启动完成后,再发布springboot开始启动事件;后面在整个try代码块中,则主要进行spring上下文环境的准备,其中包括设置环境变量、加载ApplicationContextInitializer、完成相应bean的注册等操作。最后会通知所有listener,Spring容器启动完成

总结

最后,我们用流程图画出整个run方法的流程

graph TD
Start --> 创建并准备环境参数--> 创建并初始化ApplicationContext--> 调用ApplicationContext的refresh方法,完成启动--> Stop