SpringBoot系列之启动流程1-SpringApplication的构造方法

前言

本系列开始研究 SpringBoot 的源码,之前看过的源码都忘了,还是记录博客的方式能帮助自己更深入的思考和总结。

先研究 SpringBoot 的启动流程,这个部分预计总共会有三篇,分别研究:

  • SpringApplication的构造函数

  • SpringApplication 实例的 run 方法

  • SpringBoot 启动类上的注解@SpringBootApplication

本篇是第一篇,研究SpringApplication 的构造函数。

1. 推断 web 环境

SpringBoot 应用程序的入口最终会先走到如下的构造函数中:

image-20210606135022331

构造函数的前两步代码我们直接忽略,不重要,看第三行WebApplicationType.deduceFromClasspath(),这一行是 SpringBoot 在推断当前程序的 web 环境,作用是:后续会根据这个 web 环境来决定使用哪一个 ApplicationContext 实现类。我们点进这个方法看看是怎么实现的。

image-20210606140202116

可以看到这个方法实现很简单,就是 SpringBoot 在代码里写死了一些类名,根据运行环境中是否包含这些类来判断 web 环境。运行环境分为三种:

  • Servlet
  • Reactive
  • None

2. 加载工厂配置文件

接下来是重点,SpringBoot 会加载并暂存容器的监听器和初始化器,那从哪里加载这些配置呢?我们来看代码

image-20210606140745498

getSpringFactoriesInstances这个方法里面有个不起眼的地方:SpringFactoriesLoader.loadFactoryNames(type, classLoader)。这里就是 SpringBoot “约定大于配置”原则的体现之处,即 spring.factories 机制。这个机制约定了外部组件如何将 bean 灵活的加载进 ioc 容器,并让容器感知到,类似于java 原生的SPI机制。

我们打开这个方法看一下代码:

image-20210606153442925

我们直接以 Debug 的方式来对org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories方法进行分析,看一下 Spring 是如何加载 SpringFactories 的,点进来这个类之后发现不能编辑了,原来这个类是 Spring-Core 包下的,已经不属于 SpringBoot 的范围了。

这个类的第一行有一个重要的常量,标识了Spring 约定的配置文件的目录地址,即META-INF/spring.factories

image-20210530091014938

进入loadSpringFactories方法打断点可以看到Spring就是去上述约定的地址读取工厂配置文件,而 SpringBoot 作为 Spring 工厂配置的使用方,也提供了自己的工厂配置文件

Spring 会扫描所有 jar 包下的工厂配置文件,图里扫描到了 ribbon 的

image-20210530090420127.png

我们打开 debug 信息中的工厂配置文件,发现其中有清晰的注释,表明每一个配置的用途。其中包括前面提到的Initializers和监听器Listeners。配置文件的格式是一个接口,=分割,后面是实现类,实现类可能有多个,多个的话用,\分割。

image-20210606153750992

将文件中的信息通过循环遍历都加载进来之后,会把他们都存到一个MultiValueMap中缓存起来(所谓MultiValueMap可以理解为 Map<T,List<V>>.因为“值”是由装在 List 中的多个值构成,所以称为“多值”Map),正好对应工厂配置文件中一个接口多个实现类的结构。

这里有一个关键的类是SpringFactoriesLoader,这个类用于读取 classpath 下的所有 jar 包中的工厂配置,并将这些配置放入一个内存缓存中,避免频繁IO,提供按 key 方便的获取实现类的方法SpringFactoriesLoader.loadFactoryNames(type, classLoader)。实际上 SpringBoot 内部也确实频繁的使用到了SpringFactoriesLoader.loadFactoryNames这个方法。后续在 SpringBoot 自动装配的时候,也会看到他:

image-20210601211111080

3. 推断 main 方法

这一步就比较简单了,直接根据堆栈信息遍历,查看哪个方法中有名字是 main 的方法。

总结

以上就是 SpringApplication 的构造方法源码解析,总结了一张图:
image-20210606154910392

posted @ 2021-06-19 11:46  孔令翰  阅读(304)  评论(0编辑  收藏  举报