SpringBoot 启动类源码解析(一) - 新建一个SpringApplication对象

一、启动类 - 新生成一个SpringApplication

以最简单的启动SpringBoot方式为例,

 

跟了两层以后会发现在这个地方使用静态方法创建了一个SpringApplication对象,并调用了它的 run 方法。

 

那我我们先来看下是怎么创建 SpringApplication对象的吧。

刚才这个静态方法下面又封装了一层,传入了一个null的resourceLoader,这个参数一定程度上可以理解为类加载器,传入为null时实际上最后用到类加载器的是线程的默认的,这个后面用到resourceLoader 的时候会讲。

 

再下面一层就是实际构造SpringApplication对象的构造器了。

SpringBoot 启动类源码解析(一) - 新建一个SpringApplication对象_第1张图片

 

今天我们的目标就是解释一下这个创建过程中每一行在做什么。比较简单的行就直接用注解的方式来说明了。

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
   //STEP1:设置 resourceLoader,此处应该为null
   this.resourceLoader = resourceLoader;
   //STEP2:断言,primarySources不能为空
   Assert.notNull(primarySources, "PrimarySources must not be null");
   //STEP3:将 primarySources 进行去重,放到一个成员变量里面保存
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   //STEP4:判断当前应用的类型
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   //STRP5:配置初始化器
   setInitializers((Collection) 
   getSpringFactoriesInstances(ApplicationContextInitializer.class));
   //STEP6:配置监听器
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   //STEP7:通过栈信息查找执行main方法的主类
   this.mainApplicationClass = deduceMainApplicationClass();
}

 

STEP1、2相对简单,这里就不多介绍了。STEP7可以跟一层源码,也很简单。

STEP4:

这一步是判断当前应用的类型。可以看到 WebApplicationType 是一个枚举类,主要用于判断当前应用的类型。

SpringBoot 启动类源码解析(一) - 新建一个SpringApplication对象_第2张图片

 

我们可以看到在这个枚举类里面定义了三种类型的web应用,第一种NONE代表不是Web项目,第二种就是我们常见的Servlet,第三种是Reactive。STEP4主要想做的就是判断当前的Web应用到底属于哪种类型。那么用什么方式来判断呢?我们可以从STEP4中的 WebApplicationType.deduceFromClasspath() 方法来看看

SpringBoot 启动类源码解析(一) - 新建一个SpringApplication对象_第3张图片

 

我们可以看到,这个方法判断web应用的策略就是:通过classLoader取找指定的类,再根据这个类查找的结果,来判断web应用的类型。

举个例子,我们都知道Servlet的核心类是 javax.servlet.Servlet,那我们仔细想想,反向推理的话,如果你的Web应用的classpath里面不包含这个类的话,是不是就可以断定它不是一个Web应用了?这个地方用的就是这种推理方法,品一品。

 

STEP5 & STEP6:

这两个步骤放在一起说,因为他们俩的逻辑都是一样的:从classpath中找到并读取META-INF/spring.factories文件,读取指定的配置后,实例化并设置到本地的成员变量。不同的是他们读取的配置不一样而已。

你在使用SpringBoot的时候有没有想过为什么我想集成一个Redis这种第三方组件这么简单?pom里面加一个starter,配置文件加几行就可以直接使用了?SpringBoot自动装配做了哪些事情?其实就和spring.factories 这个文件有关。这不是这篇文章讨论的重点,有兴趣的同学可以自己拓展下。

 

从方法的名字我们可以大概才出来它是干嘛的:读取 spring.factories 文件。

我们具体来分析这个方法。按照顺序我们分为四行去解析。

SpringBoot 启动类源码解析(一) - 新建一个SpringApplication对象_第4张图片

 

第一行:

 

getClassLoader()方法,获取一个类加载器。这个我们之前说过resourceLoader传的是个null,这里会用到它,我们看下这个方法的源码。

SpringBoot 启动类源码解析(一) - 新建一个SpringApplication对象_第5张图片

 

这里很明显了,如果 resourceLoader 不为空的话,那么就将这个对象中的ClassLoader返回。

我们在前面知道 resourceLoader 是null,所以看下面这个获取默认ClassLoader的逻辑。

SpringBoot 启动类源码解析(一) - 新建一个SpringApplication对象_第6张图片

 

可以看到在方法开始的时候就拿了当前线程的ClassLoader并返回,如果当前线程的ClassLoader仍为空的情况下,会返回一个系统默认的ClassLoader。

这个地方理解的比较浅显,你可以简单地认为,

getClassLoader() 这个方法无论如何都会返给你一个类加载器。

 

第二行:

 

前面有一行注释,说明这个结果是去重过的。

这个names里面放的是什么东西呢?我的理解是 spring.factories 文件中对应的配置项。

以STEP5为例,names这个Set里面放的应该是一系列配置Key为 ApplicationContextInitializer.class 全路径名 org.springframework.context.ApplicationContextInitializer 的Value。

 

上面这段话可能比较难理解,那么我们就来验证下吧。

SpringBoot 启动类源码解析(一) - 新建一个SpringApplication对象_第7张图片

 

我们在names这个地方打个断点,看下它的输出是什么?

SpringBoot 启动类源码解析(一) - 新建一个SpringApplication对象_第8张图片

 

直接启动SpringBoot就可以到这个断点,我们看到这里面有8个String类型的对象,那么这些对象从哪里来的呢?就是从spring.factories文件中来的。

下面我们来验证一下。

我们使用IDEA的全局搜索文件,可以看到有很多spring.factories。

SpringBoot 启动类源码解析(一) - 新建一个SpringApplication对象_第9张图片

 

我们选取其中一个,在

 

这个文件中按照刚才说的Key值 org.springframework.context.ApplicationContextInitializer 来搜索,

SpringBoot 启动类源码解析(一) - 新建一个SpringApplication对象_第10张图片

 

发现这几条与刚才断点调试的时候其中几条完全一致,这样我们就简单验证了一点:第二行的作用是从spring.properties文件中读取指定的配置文件。

 

这篇文章先更新到这里,后面一篇我们一起看下Spring是怎样读取 spring.properties 文件的,另外介绍下新建SpringApplication对象中STEP5&6剩下的部分。

 

你可能感兴趣的:(Java学习)