Spring Boot 打出的可执行 JAR 包和普通 JAR 包有什么区别?
Spring Boot 打出的可执行 JAR 包(Executable JAR)(通常被称为 Fat JAR 或 Uber JAR)与普通的 Java 普通 JAR 包(Plain JAR)在结构、运行机制和依赖管理上有显著的区别。
以下是核心区别的详细对比:
1. 内部目录结构不同
这是最直观的区别。如果你解压这两个 JAR 包,你会看到完全不同的结构。
普通 JAR 包:
- 结构扁平。
- 直接包含编译后的
.class文件(按包路径存放)。 - 通常不包含依赖的第三方库(如 Spring Framework、Log4j 等)。
- 结构示例:plaintext
my-app.jar ├── META-INF │ └── MANIFEST.MF └── com └── example └── MyApplication.class
Spring Boot 可执行 JAR 包:
- 结构嵌套。
BOOT-INF/classes:存放你自己编写的代码编译后的.class文件。BOOT-INF/lib:存放项目依赖的所有第三方 JAR 包(包括内嵌的 Tomcat/Jetty)。org/springframework/boot/loader:存放 Spring Boot 的类加载器代码(这是它能直接运行的关键)。- 结构示例:plaintext
my-boot-app.jar ├── META-INF │ └── MANIFEST.MF ├── org │ └── springframework │ └── boot │ └── loader ... (Spring Boot 的启动加载器) └── BOOT-INF ├── classes │ └── com/example/MyApplication.class (你的代码) └── lib ├── spring-web.jar ├── tomcat-embed-core.jar └── ... (所有依赖)
2. MANIFEST.MF 文件(入口配置)不同
JAR 包中的 META-INF/MANIFEST.MF 文件定义了 JAR 的元数据和入口点。
普通 JAR 包:
Main-Class属性直接指向你编写的主类(包含public static void main的类)。- 示例:
Main-Class: com.example.MyApplication
Spring Boot 可执行 JAR 包:
Main-Class:指向 Spring Boot 提供的特殊启动类(通常是JarLauncher)。这是因为 Java 默认的类加载器无法加载 JAR 包内部嵌套的 JAR 包,必须先运行 Spring Boot 的加载器。Start-Class:这是一个自定义属性,指向你编写的业务主类。- 示例:plaintext
Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: com.example.MyApplication
3. 类加载机制(ClassLoader)不同
普通 JAR 包:
- 使用 JDK 默认的类加载机制。
- 运行时,必须在
CLASSPATH环境变量或命令行参数中显式指定所有依赖的 JAR 路径。
Spring Boot 可执行 JAR 包:
- 使用 Spring Boot 自定义的类加载器(
LaunchedURLClassLoader)。 - 当执行
java -jar时,先启动JarLauncher,它会创建一个自定义的 ClassLoader,这个 Loader 知道如何去读取BOOT-INF/lib/下的嵌套 JAR,以及BOOT-INF/classes/下的业务代码,从而实现“自包含”运行。
- 使用 Spring Boot 自定义的类加载器(
4. 运行方式不同
普通 JAR 包:
- 不能直接独立运行(除非它不依赖任何第三方库)。
- 通常需要编写复杂的启动脚本来拼接 Classpath。
- 命令:
java -cp lib/*:my-app.jar com.example.MyApplication
Spring Boot 可执行 JAR 包:
- 开箱即用。因为它包含了内嵌的 Web 容器(如 Tomcat)和所有依赖。
- 命令:
java -jar my-boot-app.jar
5. 用途不同
普通 JAR 包:
- 通常作为依赖库(Library)被其他项目引用。
- 或者用于传统的部署模式(部署到外部安装好的 Tomcat/WebLogic 中,此时通常打成 WAR 包,但也可以是普通 JAR 配合外部 lib)。
Spring Boot 可执行 JAR 包:
- 用于微服务部署或独立应用程序。
- 注意:Spring Boot 的可执行 JAR 包不能直接作为依赖被其他项目引用(即不能在其他项目的
pom.xml中依赖它)。因为它的类都在BOOT-INF目录下,外部项目无法直接访问。 - 补充: 如果你既想让它能运行,又想让它能被依赖,需要在 Maven 插件配置中开启
classifier参数生成两个 JAR(一个 exec,一个 plain)。
总结对比表
| 特性 | 普通 JAR 包 (Plain) | Spring Boot 可执行 JAR (Executable) |
|---|---|---|
| 依赖包含 | 仅包含项目自身代码 | 包含自身代码 + 所有第三方依赖 (Fat JAR) |
| 目录结构 | 扁平 (com/example/...) |
嵌套 (BOOT-INF/classes, BOOT-INF/lib) |
| Main-Class | 指向你的主类 | 指向 Spring Boot 的 JarLauncher |
| 运行命令 | java -cp ... MainClass |
java -jar app.jar |
| 能否被依赖 | 可以 | 默认不可以 (类路径不可见) |
| 核心原理 | 标准 Java 类加载 | 自定义 ClassLoader 加载嵌套 JAR |