springboot启动流程(一)构造SpringApplication实例对象
所有文章
https://www.cnblogs.com/lay2017/p/11478237.html
启动入口
本文是springboot启动流程的第一篇,涉及的内容是SpringApplication这个对象的实例化过程。为什么从SpringApplication这个对象说起呢?我们先看一段很熟悉的代码片段
@SpringBootApplication
public class SpringBootLearnApplication { public static void main(String[] args) {
SpringApplication.run(SpringBootLearnApplication.class, args);
} }
springboot项目从一个main方法开始,main方法将会调用SpringApplication的run方法开始springboot的启动流程。所以,本文即从构造SpringApplication对象开始。
我们跟进SpringApplication的run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
这是一个静态方法,入参有两个:
1)main方法所在的类,该类后续将被作为主要的资源来使用,比如通过该类获取到basePackage;
2)main方法的命令行参数,命令行参数可以通过main传入,也就意味着可以在springboot启动的时候设置对应的参数,比如当前是dev环境、还是production环境等。
第2行代码,run方法将调用另外一个内部run方法,并返回一个ConfigurableApplicationContext,预示着spring容器将在后续过程中创建。
跟进另一个run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
run方法中先是构造了一个SpringApplication实例对象,而后调用了SpringApplication的成员方法run,这个run方法将包含springboot启动流程的核心逻辑。本文只讨论SpringApplication的实例化过程。
构造函数
跟进SpringApplication的构造函数中
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
构造函数调用了另外一个构造函数,继续跟进
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 设置资源加载器
this.resourceLoader = resourceLoader;
// 设置主要资源类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断当前应用的类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置ApplicationContext的初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置Application监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断并设置主类
this.mainApplicationClass = deduceMainApplicationClass();
}
构造过程主要包含:
1)推断当前应用类型
2)设置ApplicationContext初始化器、Application监听器
3)根据堆栈来推断当前main方法所在的主类
推断当前应用类型
WebApplicationType是一个枚举对象,枚举了可能的应用类型
public enum WebApplicationType {
/**
* 非web应用类型,不启动web容器
*/
NONE,
/**
* 基于Servlet的web应用,将启动Servlet的嵌入式web容器
*/
SERVLET,
/**
* 基于reactive的web应用,将启动reactive的嵌入式web容器
*/
REACTIVE;
// 省略...
}
deduceFromClasspath方法将会推断出当前应用属于以上三个枚举实例的哪一个,跟进方法
static WebApplicationType deduceFromClasspath() {
// 类路径中是否包含DispatcherHandler,且不包含DispatcherServlet,也不包含ServletContainer,那么是reactive应用
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 如果Servlet或者ConfigurableWebApplicationContext不存在,那么就是非web应用
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 否则都是Servlet的web应用
return WebApplicationType.SERVLET;
}
推断过程将根据类路径中是否有指示性的类来判断
设置ApplicationContext初始化器、Application监听器
getSpringFactoriesInstances(ApplicationContextInitializer.class)
这个方法调用将会从META-INF/spring.factories配置文件中找到所有ApplicationContextInitializer接口对应的实现类配置,然后通过反射机制构造出对应的实例对象。
getSpringFactoriesInstances(ApplicationListener.class)
这个方法也是一样的做法,将会获取ApplicationListener接口的所有配置实例对象
有关于如何从spring.factories配置文件中获取配置并构造出实例对象请看:spring.factories配置文件的工厂模式
根据堆栈来推断当前main方法所在的主类
构造SpringApplication还有最后一步,推断出main方法所在的主类。我们跟进deduceMainApplicationClass方法
private Class<?> deduceMainApplicationClass() {
try {
// 获取堆栈链路
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
// 遍历每一个栈帧信息
for (StackTraceElement stackTraceElement : stackTrace) {
// 如果该栈帧对应的方法名等于main
if ("main".equals(stackTraceElement.getMethodName())) {
// 获取该类的class对象
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
该方法采用遍历栈帧的方式来判断最终main方法落在哪个栈帧上,并通过forName来获取该类
总结
到这里本文就结束了,核心点就在于SpringApplication的实例化,可以看出最主要的就是做了应用类型的推断,后面的Application创建、Environment创建也会基于该类型。
springboot启动流程(一)构造SpringApplication实例对象的更多相关文章
- springboot启动流程(目录)
springboot出现有段时间了,不过却一直没有怎么去更多地了解它.一方面是工作的原因,另一方面是原来觉得是否有这个必要,但要持续做java似乎最终逃不开要去了解它的命运.于是考虑花一段时间去学习一 ...
- SpringBoot启动流程分析(二):SpringApplication的run方法
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(一):SpringApplication类初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- springboot启动流程(二)SpringApplication run方法核心逻辑
所有文章 https://www.cnblogs.com/lay2017/p/11478237.html run方法逻辑 在上一篇文章中,我们看到SpringApplication的静态方法最终是去构 ...
- SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程解析
写在前面: 由于该系统是底层系统,以微服务形式对外暴露dubbo服务,所以本流程中SpringBoot不基于jetty或者tomcat等容器启动方式发布服务,而是以执行程序方式启动来发布(参考下图ke ...
- SpringBoot启动流程分析(六):IoC容器依赖注入
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程及其原理
Spring Boot.Spring MVC 和 Spring 有什么区别? 分别描述各自的特征: Spring 框架就像一个家族,有众多衍生产品例如 boot.security.jpa等等:但他们的 ...
- springboot启动流程(七)ioc容器refresh过程(上篇)
所有文章 https://www.cnblogs.com/lay2017/p/11478237.html 正文 在前面的几篇文章中,我们看到Environment创建.application配置文件的 ...
随机推荐
- Docs-.NET-C#-指南-语言参考-预处理器指令:#endif(C# 参考)
ylbtech-Docs-.NET-C#-指南-语言参考-预处理器指令:#endif(C# 参考) 1.返回顶部 1. #endif(C# 参考) 2015/07/20 #endif 指定条件指令的末 ...
- android.support.design库的引用和冲突解决
android.support.design库的引用和冲突解决 转 https://www.jianshu.com/p/2a0a2af9f2b4 最近在工程中使用到android.support.de ...
- 批量删除Maven 仓库未下载成功.lastupdate 的文件
Windows: @echo off echo 开始... for /f "delims=" %%i in ('dir /b /s "./*lastUpdated&quo ...
- MySQL数据库之sql_mode解释
在MySQL5.6中,默认的SQL模式为:NO_ENGINE_SUBSTITUTION, 而在MySQL5.7中默认的SQL模式为:ONLY_FULL_GROUP_BY, STRICT_TRANS_T ...
- HTML滚动时位置固定
现在显示器一般都是宽屏,网页两端常常会留白. 两边可能会放一些推荐.标签或是导航什么的辅助模块. 现在有的网站页面内容过长时,用户将滚动条向下拉时,拉到一定程度,左右两侧的辅助模块就会固定在指定位置, ...
- 第十四章 SSL——《跟我学Shiro》
目录贴:跟我学Shiro目录贴 对于SSL的支持,Shiro只是判断当前url是否需要SSL登录,如果需要自动重定向到https进行访问. 首先生成数字证书,生成证书到D:\localhost.key ...
- iOS——使用FMDB进行数据库操作(转载)
iOS 使用FMDB进行数据库操作 https://github.com/ccgus/fmdb [摘要]本文介绍iOS 使用FMDB进行数据库操作,并提供详细的示例代码供参考. FMDB 使用方法 A ...
- python多进程实例详解
写在前面:python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程.Python提供了非常好用的多进程包multiprocessing ...
- sas(Serial Attached SCSI)技术详解
1.什么是SAS 即串行SCSI技术,是一种磁盘连接技术,它综合了并行SCSI和串行连接技术(FC,SSA,IEEE1394)的优势,以串行通讯协议为协议基础架构,采用SCSI-3扩展指令集,并兼容S ...
- S1. Android 功能大全
[概述] 这篇文章主要描述安卓开发中可能实现的功能点. [准备工作] IDE:Android Studio,简单操作 如何创建一个 Android 项目 Android 项目结构分析 Androidm ...