概述
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
的启动类等信息。每个 Javajar
包应该是都有这个文件的,参考 Oracle 官方对于jar
的说明,里面有一个Main-Class
配置用于指定启动类org.springframework.boot.loader
目录,也就是 Spring Boot 的spring-boot-loader
工具模块,它就是java -jar xxx.jar
启动 Spring Boot 项目的秘密所在,上面的Main-Class
指定的就是该工具模块中的一个类- https://www.jianshu.com/p/b04a54873f3b
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) {