-
Spring Boot 提供了 Maven 插件 spring-boot-maven-plugin,可以方便的将 Spring Boot 项目打成 jar 包或者 war 包。
-
Jar包的结构组成
-
① META-INF 目录:通过 MANIFEST.MF 文件提供 jar 包的元数据,声明了 jar 的启动类。
-
② org 目录:为 Spring Boot 提供的 spring-boot-loader 项目,它是 java -jar 启动 Spring Boot 项目的秘密所在,也是稍后我们将深入了解的部分。
Spring Boot Loader provides the secret sauce that allows you to build a single jar file that can be launched using java -jar. Generally you will not need to use spring-boot-loader directly, but instead work with the Gradle or Maven plugin.
-
③ BOOT-INF/lib 目录:我们 Spring Boot 项目中引入的依赖的 jar 包们。spring-boot-loader 项目很大的一个作用,就是解决 jar 包里嵌套 jar 的情况,如何加载到其中的类。
spring-boot-loader 项目需要解决两个问题:
- 第一,如何引导执行我们创建的 Spring Boot 应用的启动类,例如上述图中的 Application 类。
- 第二,如何加载
BOOT-INF/class 目录下的类,以及 BOOT-INF/lib 目录下内嵌的 jar 包中的类。
-
④ BOOT-INF/classes 目录:我们在 Spring Boot 项目中 Java 类所编译的 .class、配置文件等等。
-
META-INF/MANIFEST.MF 文件,里面的内容如下:
Manifest-Version: 1.0
Implementation-Title: lab-39-demo
Implementation-Version: 2.2.2.RELEASE
Start-Class: cn.iocoder.springboot.lab39.skywalkingdemo.Application
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.2.2.RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher
-
实际是一个 Properties 配置文件,每一行都是一个配置项目。重点来看看两个配置项:
Main-Class 配置项:Java 规定的 jar 包的启动类,这里设置为 spring-boot-loader 项目的 JarLauncher 类,进行 Spring Boot 应用的启动。Start-Class 配置项:Spring Boot 规定的主启动类,这里设置为我们定义的 Application 类。
小知识补充:为什么会有 Main-Class/Start-Class 配置项呢?因为我们是通过 Spring Boot 提供的 Maven 插件 spring-boot-maven-plugin 进行打包,该插件将该配置项写入到 MANIFEST.MF 中,从而能让 spring-boot-loader 能够引导启动 Spring Boot 应用。
- 直接运行
Start-Class 配置项的主启动类(main函数)是无法启动的(提示找不到或无法加载主类),主要是因为打包成的jar的不符合java默认加载jar包的规则(/WEB-INF/classes下的class文件,而Start-Class的Aplication类被打包在 BOOT-INF/classes下,其次Java 规定可执行器的 jar 包禁止嵌套其它 jar 包,而maven项目需要加载BOOT-INF/lib 目录下Spring Boot 应用依赖的所有 jar 包),所以需要通过spring-boot-loader 项目自定义实现了 ClassLoader 实现类 LaunchedURLClassLoader,支持加载 BOOT-INF/classes 目录下的 .class 文件,以及 BOOT-INF/lib 目录下的 jar 包。
-
JarLauncher 类是针对 Spring Boot jar 包的启动类,整体类图如下所示:

代码如下:
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) {
if (entry.isDirectory()) {
return entry.getName().equals(BOOT_INF_CLASSES);
}
return entry.getName().startsWith(BOOT_INF_LIB);
}
public static void main(String[] args) throws Exception {
new JarLauncher().launch(args);
}
}
通过 #main(String[] args) 方法,创建 JarLauncher 对象,并调用其 #launch(String[] args) 方法进行启动。整体的启动逻辑,其实是由父类 Launcher 所提供。
-
简单来说,就是做一个可以读取 jar 包中类的加载器,保证 BOOT-INF/lib 目录下的类和 BOOT-classes 内嵌的 jar 中的类能够被正常加载到,之后执行 Spring Boot 应用的启动。
-
LaunchedURLClassLoader 是 spring-boot-loader 项目自定义的类加载器,实现对 jar 包中 META-INF/classes 目录下的类和 META-INF/lib 内嵌的 jar 包中的类的加载。
-
总体来说,Spring Boot jar 启动的原理是非常清晰的,整体如下图所示:
