前言
为什么SpringBoot可以做到一键启动?甚至可以通过java -jar一条命令启动一个web项目,再也不用操心如何搭建tomcat等相关web容器。SpringBoot带给我们这么多便利的背后,究竟做了什么呢?只有了解了底层实现原理,才能更好的掌握该项技术带来的好处以及性能调优,本篇文章带大家一探究竟。
启动流程图
启动过程核心过程
1、初始化SpringApplication,期间主要做了如下事情:
- 判断当前容器属于什么web类型,默认不加干预的话,是servlet类型;
- 从spring.factories目录下,读取了所有ApplicationListener监听器,供后续各种发布事件时,对应监听器需要去各司其职;
- 找到main方法所在类;
2、运行容器,期间主要做了如下事情:
- 开启StopWatch,记录服务启动时间;
- 启动所有读取到的listener;
- 发布ApplicationEnvironmentPreparedEvent(环境准备事件),其中监听器监听到具体事件后,会 读取系统参数,其中一个比较核心的监听器,ConfigFileApplicationListener,会去读取application.yml/application.properties等配置文件,封装到Environment变量中;
- 创建context(上下文容器),上下文容器,你可能比较陌生,但是学习SpringIOC源码时,程序入口定义时,你是否也会new AnnotationConfigCApplicationContext(xxx.class),你可以将这个理解为上下文容器,没有这个容器,就没法进行后续一系列Spring的操作;我们创建context容器时,会根据前面的web类型来选择对应的容器实现,比如servlet对应的为AnnotationConfigServletWebServerApplicationContext;
- 准备上下文,其中比较核心的点是,将启动类注册到beandifinitionMap当中,其次发布ApplicationPreparedEvent事件;
- 刷新上下文容器,创建内置web容器,比如你是servlet应用,创建tomcat容器;
- 容器启动完毕,打印耗时;
- 发布ApplicationStartedEvent事件,代表容器启动成功,处于活动状态;
- 发布ApplicationReadyEvent事件,代表容器可以开始接收请求;
- 如果启动过程中报错,发布ApplicationFailedEvent事件;
java -jar启动springboot程序背后的秘密
Oracle官方定义的解释
具体链接:docs.oracle.com/javase/8/do…
翻译过来的意思是:
使用-jar参数时,jar后面接的参数是具体jar文件,该jar包含class和资源文件,在mainfest文件中有Main-Class的定义;Main-Class中指定了启动类;
总结一下:
java -jar会去找jar包中的mainfest文件,在里面找到具体的启动类;
本地编译的springboot工程mainfest文件
通过java -jar在本地启动springboot工程
jar包目录结构
springboot-0.0.1-SNAPSHOT.jar
├─BOOT-INF
│ ├─classes
│ │ ├─应用程序类
│ │ └─i18n
│ └─lib
├─META-INF
│ └─MANIFEST.MF
└─org
└─springframework
└─boot
└─loader
├─springboot启动程序
这里直接说下结论,有兴趣的朋友可以去深度研究下
- JarLauncher通过加载BOOT-INF/classes及BOOT-INF/lib目录下的jar,实现fat jar启动;
- SpringBoot通过扩展JarFile、JarURLConnection及URLStreamHandler,实现了jar in jar中资源的加载;
- SpringBoot通过扩展URLClassLoader-LauncherURLClassLoader,实现了jar in jar中文件的加载;
- WarLauncher通过加载WEB-INF/classes目录及WEB-INF/lib-provided目录下的jar文件,实现了war文件的直接启动及web容器中的启动;
如何使用外置servlet容器部署SpringBoot工程
前言
某些客户可能要求你必须使用某个版本的servlet容器部署springboot工程,这个时候,就不能使用springboot内置servlet容器了
演示使用外置servlet容器部署springboot工程
- 设置当前maven项目打包方式
<packaging>war</packaging>
- 让tomcat相关依赖不参数打包部署,因为外置tomcat服务器已经有这些jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
- SpringBoot启动类继承SpringBootServletInitializer
@SpringBootApplication
public class SpringbootApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringbootApplication.class);
}
}
- 使用maven打包成war包后,使用外置tomcat去部署
- 测试是否部署成功
分析下外置tomcat是如何运行SpringBoot工程
Servlet3.0官方文档:
Embedded servlet containers do not directly execute the Servlet 3.0+ `javax.servlet.ServletContainerInitializer` interface or Spring's `org.springframework.web.WebApplicationInitializer` interface.
This is an intentional design decision intended to reduce the risk that third party libraries designed to run inside a war may break Spring Boot applications.
上述这句话大概的意思就是:
当servet容器启动时候,就会去META-INF/services文件下找到javax.servlet.ServletContainerInitializer文件,这个文件里面就包含接口(javax.servlet.ServletContainerInitializer)的具体实现类(具体运用了SPI机制),从而创建它的实例调用onStartup方法;
具体工作流程图:
结尾
该网站对svg兼容没有做到很好,没有将svg完整效果图全部展示出来,以下为具体svg原件,可以下载下来,在本地用浏览器查看最终效果图;
www.processon.com/view/link/6…
www.processon.com/view/link/6…