入口:SpringApplication.run(Application.class,args);

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

    Collection exceptionReporters;
    try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
      this.configureIgnoreBeanInfo(environment);
      Banner printedBanner = this.printBanner(environment);
      context = this.createApplicationContext();
      exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
      this.prepareContext(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, exceptionReporters, listeners);
      throw new IllegalStateException(var10);
    }

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

1、将当前启动类的字节码传入(主要目的是传入@SpringBootApplication这个注解),以及main函数的args参数。

2、开启计时器

StopWatch stopWatch = new StopWatch();
stopWatch.start();

3、声明context容器

ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();

4、创建并启动主入口的监听器

SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();

5、解析应用程序的启动参数

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

6、创建并配置启动环境(加载配置文件)

ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);

7、排除不需要的环境

this.configureIgnoreBeanInfo(environment);

8、输出banner

Banner printedBanner = this.printBanner(environment);

9、根据webApplicationType创建应用上下文

context = this.createApplicationContext();

10、获取类的加载器,加载spring.factories的全类名集合,反射获取class创建实例对象,并根据优先级返回列表

 exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);

11、预处理上下文,这一步主要是在容器刷新之前的准备动作。设置容器环境,包括各种变量等等,其中包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。

 this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);

12、刷新上下文,开启刷新spring容器,通过refresh方法对整个IOC容器的初始化(包括bean资源的定位,解析,注册等等),同时向JVM运行时注册一个关机钩子,在JVM关机时会关闭这个上下文,除非当时它已经关闭。

this.refreshContext(context);

13、空方法,扩展接口,设计模式中的模板方法,默认为空实现。如果有自定义需求,可以重写方法。比如打印一些启动结束log,或者一些其他后置处理。

this.afterRefresh(context, applicationArguments);

14、停止计时器

stopWatch.stop();

15、获取EventPublishingRunListener监听器,并执行其started方法,并且将创建的spring容器传进去了,创建一个ApplicationStartedEvent事件,并执行ConfigurableApplicationContext的publishEvent方法,也就是说这里是在Spring容器中发布事件,并不是在SpringApplication中发布事件,和前面的starting是不同的,前面的starting是直接向SpringApplication中的监听器发布启动事件。

listeners.started(context);

16、用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序。其中,SpringBoot提供的执行器接口有ApplicationRunner和CommonLineRunner两种,在使用时只需要自定义一个执行器类实现其中一个接口并重写对应的run()方法接口,然后Spring Boot项目启动后会立即执行这些特定程序。

 this.callRunners(context, applicationArguments);