-
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
启动的原理是非常清晰的,整体如下图所示: