该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好!
建议读者私信我领取相关源码注释SpringBoot源码分析结合阅读!
最好对 Spring 源码有一定的了解,如果该篇内容对您有帮助,麻烦点个“赞”,也可以关注小编,感激不尽~
Spring Boot 提供了 Maven 插件 spring-boot-maven-plugin,可以很方便的将我们的 Spring Boot 项目打成 jar 包或者 war 包。
考虑到部署的便利性,我们绝大多数(99.99%)的场景下,都会选择打成 jar 包,这样一来,我们就无需将项目部署于 Tomcat、Jetty 等 Servlet 容器中。
那么,通过 Spring Boot 插件生成的 jar 包是如何运行,并启动 Spring Boot 应用的呢?这个就是本文的目的,我们一起来弄懂 Spring Boot jar 包的运行原理。
这里,我通过 Spring Boot Maven Plugin 生成了一个 jar 包,其里面的结构如下所示:
- BOOT-INF 目录,里面保存了我们自己 Spring Boot 项目编译后的所有文件,其中 classes 目录下面就是编译后的
.class 文件,包括项目中的配置文件等,lib 目录下就是我们引入的第三方依赖 - META-INF 目录,通过 MANIFEST.MF 文件提供 jar 包的元数据,声明 jar 的启动类等信息。每个 Java jar
包应该是都有这个文件的,参考 Oracle 官方对于 jar 的说明,里面有一个 Main-Class 配置用于指定启动类 - org.springframework.boot.loader 目录,也就是 Spring Boot 的
spring-boot-loader 工具模块,它就是 java -jar xxx.jar 启动 Spring Boot
项目的秘密所在,上面的 Main-Class 指定的就是该工具模块中的一个类
MANIFEST.MF
META-INF/MANIFEST.MF 文件如下:
Manifest-Version: 1.0
Implementation-Title: spring-boot-study
Implementation-Version: 1.0.0-SNAPSHOT
Built-By: jingping
Implementation-Vendor-Id: org.springframework.boot.demo
Spring-Boot-Version: 2.0.3.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncher # spring-boot-loader 中的启动类
Start-Class: org.springframework.boot.demo.Application # 你的 Spring Boot 项目中的启动类
Spring-Boot-Classes: BOOT-INF/classes/ # 你的 Spring Boot 项目编译后的 .class 文件所在目录
Spring-Boot-Lib: BOOT-INF/lib/ # 你的 Spring Boot 项目所引入的第三方依赖所在目录
Created-By: Apache Maven 3.6.3
Build-Jdk: 1.8.0_251
Implementation-URL: https://projects.spring.io/spring-boot/#/spring-boot-starter-parent/info-dependencies/dwzq-info/info-stock-project/sp-provider
参考 Oracle 官方对该的说明:
Main-Class:Java 规定的 jar 包的启动类,这里设置为 spring-boot-loader 项目的 JarLauncher 类,进行 Spring Boot 应用的启动
Start-Class:Spring Boot 规定的主启动类,这里通过 Spring Boot Maven Plugin 插件打包时,会设置为我们定义的 Application 启动类
为什么不直接将我们的 Application 启动类设置为 Main-Class 启动呢?
因为通过 Spring Boot Maven Plugin 插件打包后的 jar 包,我们的 .class 文件在
BOOT-INF/classes/ 目录下,在 Java 默认的 jar 包加载规则下找不到我们的 Application
启动类,也就需要通过 JarLauncher 启动加载。当然,还有一个原因,Java 规定可执行器的 jar 包禁止嵌套其它 jar 包,在 BOOT-INF/lib 目录下有我们 Spring
Boot 应用依赖的所有第三方 jar 包,因此spring-boot-loader 项目自定义实现了 ClassLoader 实现类
LaunchedURLClassLoader,支持加载 BOOT-INF/classes 目录下的 .class 文件,以及 BOOT-INF/lib 目录下的 jar 包。
接下来,我们一起来看看 Spring Boot 的 JarLauncher 这个类
1.JarLauncher
类图:
上面的 WarLauncher 是针对war包的启动类,和 JarLauncher 差不多,感兴趣的可以看一看,这里我们直接来看到 JarLauncher 这个类
public class JarLauncher extends ExecutableArchiveLauncher {
static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
static final String BOOT_INF_LIB = "BOOT-INF/lib/";
public JarLauncher() {
}
protected JarLauncher(Archive archive) {
super(archive);
}
@Override
protected boolean isNestedArchive(Archive.Entry entry) {
// 只接受 `BOOT-INF/classes/` 目录
if (entry.isDirectory()) {
return entry.getName().equals(BOOT_INF_CLASSES);
}
// 只接受 `BOOT-INF/lib/` 目录下的 jar 包
return entry.getName().startsWith(BOOT_INF_LIB);
}
/**
* 这里是 java -jar 启动 SpringBoot 打包后的 jar 包的入口
* 可查看 jar 包中的 META-INF/MANIFEST.MF 文件(该文件用于对 Java 应用进行配置)
* 参考 Oracle 官方对于 jar 的说明(https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html)
* 该文件其中会有一个配置项:Main-Class: org.springframework.boot.loader.JarLauncher
* 这个配置表示会调用 JarLauncher#main(Str