深入SpringBoot源码(一)从SpringApplication的构造方法入门源码
-
- 前言
- 学习SpringBoot源码的入口点
- SpringApplication的构造方法
- 初识ResourceLoader、Resource
- WebApplicationType枚举
- 初识BootstrapRegistryInitializer、BootstrapRegistry
- 初识getSpringFactoriesInstances方法
- 初识ApplicationContextInitializer、ConfigurableApplicationContext
- 初识ApplicationListener、ApplicationEvent、PayloadApplicationEvent
- deduceMainApplicationClass方法
前言
本文涉及代码所使用的SpringBoot版本为2.6.3(这是截至此文发布时Spring Cloud Alibaba最新版本适配的SpringBoot版本,点击此处可查看Spring Cloud Alibaba与SpringBoot的版本适配情况),如果未来版本的SpringBoot源码有较大变动,不建议再学习本文。
学习SpringBoot源码的入口点
@SpringBootApplication
public class TestMain {
public static void main(String[] args) {
SpringApplication.run(TestMain.class, args);
}
}
上面这段代码相信使用SpringBoot的大家再熟悉不过了,是SpringBoot的应用程序启动入口,这也是SpringApplication(org.springframework.boot.SpringApplication)的最主要作用。SpringApplication.run方法是本系列文章讲解源码的入口点,接下来我们就来一层一层深挖SpringApplication.run背后到底做了什么能让我们的SpringBoot应用正常启动并提供服务。
我们先来看一下SpringApplication的类层次性结构。(这是IDEA的类层次性结构视图,默认快捷键Ctrl+H)
可以看到SpringApplication没有继承除Object以外的任何类也没有实现任何接口,不需要考虑复杂的类层次性关系,这也是其适合作为学习SpringBoot源码入口点的主要原因。
SpringApplication的构造方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] {
primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
上面就是SpringApplication的run方法源码(run方法到底做了什么以及run方法返回的ConfigurableApplicationContext是什么会在以后文章中详解,因为要理解这些需要依赖后面讲的源码。本文的关注点在SpringApplication的构造方法),可以看到最终的run方法调用了构造方法new SpringApplication(primarySources)。
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
构造方法SpringApplication(Class<?>... primarySources)最终调用SpringApplication(ResourceLoader resourceLoader, Class<?>… primarySources)。下面来看SpringApplication构造方法每步代码的作用。
初识ResourceLoader、Resource
SpringApplication(ResourceLoader resourceLoader, Class<?>… primarySources)(由于传入的是ResourceLoader是null,在构造函数中并看不出具体作用,所以本文先初步了解接口方法,后面的文章会讲解ResourceLoader的实现类源码)
ResourceLoader(org.springframework.core.io.ResourceLoader)是用于加载资源(例如,类路径或文件系统资源)的策略接口。
public interface ResourceLoader {
/** 从类路径加载的伪 URL 前缀:“classpath:” */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
/**
* 返回指定资源位置的Resource句柄。
*/
Resource getResource(String location);
/**
* 返回此ResourceLoader使用的ClassLoader 。
*/
@Nullable
ClassLoader getClassLoader();
}
ResourceLoader的层次性关系(子类型):
Resource(org.springframework.core.io.Resource) 是从底层资源的实际类型(例如文件或类路径资源)中抽象出来的资源描述符的接口。
public interface Resource extends InputStreamSource {
/**
* 确定此资源是否实际以物理形式存在
*/
boolean exists();
/**
* 指示是否可以通过getInputStream()读取此资源的非空内容
*/
default boolean isReadable() {
return exists();
}
/**
* 指示此资源是否表示具有打开流的句柄。
*/
default boolean isOpen() {
return false;
}
/**
* 确定此资源是否代表文件系统中的文件。
*/
default boolean isFile() {
return false;
}
/**
* 返回此资源的 URL 句柄。
*/
URL getURL() throws IOException;
/**
* 返回此资源的 URI 句柄。
*/
URI getURI() throws IOException;
/**
* 返回此资源的文件句柄。
*/
File getFile() throws IOException;
/**
* 返回一个ReadableByteChannel 。
*/
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
/**
* 确定此资源的内容长度。
*/
long contentLength() throws IOException;
/**
* 确定此资源的最后修改时间戳。
*/
long lastModified() throws IOException;
/**
* 创建与此资源相关的资源。
*/
Resource createRelative(String relativePath) throws IOException;
/**
* 确定此资源的文件名,即通常是路径的最后部分:例如,“myfile.txt”。
* 如果这种类型的资源没有文件名,则返回null 。
*/
@Nullable
String getFilename();
/**
* 返回此资源的描述,用于在使用资源时输出错误。
*/
String getDescription();
}
Resource的层次性关系(父类型):