SpringBoot2启动流程


title: SpringBoot2.StartProcess

StartProcess提纲

  • 启动流程过程
  • 具体源码分析

SpringBoot如果要说是SpringFrameWork的封装那么也不全是,个人认为从思想上来说是两个东西。
SpringBoot主要具备如下特性
1.自动装配
2.内置tomcat等web容器
3.与devops天然集成,这也可以认为是微服务的特点。

启动流程过程

首先创建SpringApplication对象期间初始化程序运行环境拉取listener以及获取ApplicationContextInitializer
接着会运行run方法具体会执行如下步骤
1.获取并启动监听器
2.构造容器环境,加载application配置文件或者自定义配置文件位置
3.创建容器
4.准备预处理容器
5.刷新容器
6.刷新容器后调用扩展接口
经过上面几步整个程序就运行起来了

下面咱们看下代码

具体源码分析

下面是一段程序的启动入口代码,这块代码其实可以自己自定义new一个SpringApplication对象启动

@SpringBootApplication
public class DemoApplication {

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

}

咱们跟进run方法中去,会看到下面这一段代码,。这里如上面开头所说有两个步骤
1.初始化SpringApplication对象
2.执行run方法

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
    String[] args) {
  return new SpringApplication(primarySources).run(args);
}

先看初始化SpringApplication,咱们点进去看一下构造函数干了些啥

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    //resourceLoader加载配置资源但是没在这初始化,后面再说
    this.resourceLoader = resourceLoader;
    //默认是启动类,不过有可能你自定义了一个配置类放到main class运行就需要配置一下
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //springApplication配置类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //设置servlet环境
    this.webApplicationType = deduceWebApplicationType();
    获取ApplicationContextInitializer,也是在这里开始首次加载spring.factories文件
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    //获取监听器,这里是第二次加载spring.factories文件
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

上面的重点其实都在setInitializers与setListeners
这两个的初始化其实是SPI的Spring实现
getSpringFactoriesInstances这个方法挺有意思的
原理就是获取配置文件下type是哪一类的key配置的class名,然后通过反射实例化。

咱们继续往下看run方法
上一段代码,在代码里面有注释。先看看

/**
   * Run the Spring application, creating and refreshing a new
   * {@link ApplicationContext}.
   * @param args the application arguments (usually passed from a Java main method)
   * @return a running {@link ApplicationContext}
   */
  public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    //第一步:获取并启动监听器.org.springframework.boot.context.event.EventPublishingRunListener
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
          args);
      //第二步:构造容器环境,加载application配置文件或者自定义配置文件位置
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
          applicationArguments);
      //设置需要忽略的bean
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      //第三步:创建容器
      context = createApplicationContext();
      //第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
      exceptionReporters = getSpringFactoriesInstances(
          SpringBootExceptionReporter.class,
          new Class[] { ConfigurableApplicationContext.class }, context);
      //第五步:准备容器
      prepareContext(context, environment, listeners, applicationArguments,
          printedBanner);
      //第六步:刷新容器
      refreshContext(context);
      //第七步:刷新容器后的扩展接口
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass)
            .logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
      callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
    }

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

首先监听器是一个好东西,每当程序执行到哪个步骤的时候就根据相应的Event去执行相应的方法
不过需要将实现了的接口类都加载进来才会去执行,这里的这个监听器是SpringApplicationRunListener
也可以认为是第一个执行的监听器,钩子类监听器拉起来一堆的其它监听
接着就构造Environment
然后就是容器的创建,预处理准备容器,刷新容器
最后执行刷新后的容器扩展接口整个程序如果没有报错则会启动。

总结一下
这篇是先大概将SpringBoot的大流程说一下,从俯视角度看全局做到心中有数。
我们后续再详细拆分一小块一小块的细写。
不然一上来就每行代码的过,这样看Spring源码容易陷进去出不来。
导致最后感觉啥都不会

欢迎扫码加入知识星球继续讨论
avatar

你可能感兴趣的:(SpringBoot2启动流程)