Spring Boot JAR 包结构详解:从内部组成到启动原理
Spring Boot 项目通过 spring-boot-maven-plugin 打包生成的 JAR 包,并非普通 Java JAR,而是可执行 JAR(Executable JAR)—— 其内部结构经过特殊设计,包含了应用代码、依赖库、启动器和配置信息,确保能通过 java -jar 直接执行。从 “目录结构解析→核心文件作用→启动流程关联” 三个维度,彻底讲透 Spring Boot JAR 包的内部逻辑。
Spring Boot JAR 包整体结构
首先,我们通过一个标准的 Spring Boot JAR 包解压后的目录结构,直观理解其组成:
1 | demo-0.0.1-SNAPSHOT.jar # Spring Boot 可执行 JAR |
这个结构的核心是 “分层存放”:将 “应用代码”“依赖库”“启动工具” 分离,既保证了可执行性,又便于管理资源。
核心目录详解:每个目录的作用
1. BOOT-INF/:应用与依赖的核心存放地
BOOT-INF 是 Spring Boot JAR 包的 “心脏”,所有与项目业务相关的内容都在这里,分为 classes 和 lib 两个子目录。
(1)BOOT-INF/classes/:应用自身的代码和配置
- 内容:
- 项目编译后的
.class文件(对应你编写的 Controller、Service、主程序类等); - 应用配置文件(
application.yml/application.properties、自定义 YML/Properties 文件); - 静态资源(
static/目录下的 CSS、JS、图片,若项目包含); - 模板文件(
templates/目录下的 Thymeleaf/Freemarker 文件,若使用模板引擎)。
- 项目编译后的
- 作用:这是你编写的 “业务代码和配置” 的存放位置,相当于传统 Java 项目的
target/classes目录,但被 Spring Boot 统一归入BOOT-INF下,便于启动器识别。 - 关键文件:
DemoApplication.class(你的主程序类)——MANIFEST.MF中的Start-Class就是指向这个类的全路径(如com.zhanghe.study.springboot.DemoApplication)。
(2)BOOT-INF/lib/:项目依赖的第三方 JAR
- 内容:Maven 依赖的所有第三方库(包括 Spring Boot Starter 依赖和自定义依赖),每个依赖都是独立的 JAR 文件。
- 作用:解决了 “普通 JAR 需手动指定类路径” 的问题 ——Spring Boot 启动器会自动扫描
lib目录下的所有 JAR,加载其中的类,无需用户手动配置-classpath。 - 示例:若你的项目引入了
spring-boot-starter-web,则lib目录会包含spring-webmvc.jar、tomcat-embed-core.jar等依赖;若引入了mybatis-spring-boot-starter,则会包含mybatis.jar、mybatis-spring.jar等。
2. META-INF/:元信息与启动配置
META-INF 是 Java 标准的元信息目录,Spring Boot 在这里放置了关键的启动配置文件 MANIFEST.MF 和 Maven 构建信息。
(1)META-INF/MANIFEST.MF:启动的 “指挥中心”
这是整个 JAR 包中最关键的文件 —— 它告诉 JVM 如何启动应用,我们逐行解析核心配置:
| 配置项 | 示例值 | 核心作用 |
|---|---|---|
Manifest-Version |
1.0 | JAR 清单文件的版本(固定为 1.0) |
Main-Class |
org.springframework.boot.loader.JarLauncher | JVM 实际执行的 “启动器类”—— 入口类,非你的主程序类 |
Start-Class |
com.zhanghe.study.springboot.DemoApplication | 你编写的 “应用主程序类”—— 含 main 方法的类 |
Spring-Boot-Classes |
BOOT-INF/classes/ | 应用代码的存放路径(启动器从这里加载你的类) |
Spring-Boot-Lib |
BOOT-INF/lib/ | 依赖库的存放路径(启动器从这里加载第三方类) |
Spring-Boot-Version |
1.5.9.RELEASE | Spring Boot 版本(启动器依赖此版本适配) |
核心逻辑:
JVM 执行 java -jar 时,首先读取 Main-Class,启动 JarLauncher;JarLauncher 再根据 Spring-Boot-Classes 和 Spring-Boot-Lib 找到应用代码和依赖,最后通过反射调用 Start-Class(你的主程序类)的 main 方法,启动 Spring Boot 应用。
(2)META-INF/maven/:Maven 构建信息(可选)
- 内容:项目的
pom.xml副本和pom.properties(包含groupId、artifactId、version等构建属性)。 - 作用:仅用于记录构建信息,不影响应用启动 —— 若需查看 JAR 包对应的源码版本或依赖,可解压后查看此目录下的
pom.xml。
3. org/springframework/boot/loader/:Spring Boot 启动器工具类
这是 Spring Boot 内置的 “启动工具包”,包含了让 JAR 包可执行的核心逻辑,关键类有三个:
(1)JarLauncher.class:JAR 包的启动器
- 作用:Main-Class指向的类,是 JVM 执行的入口 —— 它的核心任务是:
- 初始化自定义类加载器
LaunchedURLClassLoader; - 告诉类加载器从
BOOT-INF/classes/加载应用类,从BOOT-INF/lib/加载依赖类; - 反射调用
Start-Class(你的主程序类)的main方法。
- 初始化自定义类加载器
- 对应 WAR 包:若项目打包为 WAR(部署到外置 Tomcat),则启动器会替换为
WarLauncher.class,逻辑类似但适配 WAR 结构。
(2)LaunchedURLClassLoader.class:自定义类加载器
- 作用:解决 “普通类加载器无法识别 BOOT-INF 结构” 的问题 ——Java 默认的
AppClassLoader只能加载 JAR 根目录的类,而LaunchedURLClassLoader能识别BOOT-INF/classes/和BOOT-INF/lib/,并加载其中的类。 - 关键能力:将
BOOT-INF/classes/视为 “类路径根目录”,将BOOT-INF/lib/下的每个 JAR 视为 “扩展类路径”,确保应用代码和依赖能被正确加载。
(3)其他辅助类
如 JarFile(解析 JAR 包结构)、Launcher(启动器抽象基类)等,共同支撑启动流程。
JAR 包启动流程:从 java -jar 到应用启动
结合 JAR 包结构和 MANIFEST.MF 配置,我们梳理 java -jar demo.jar 执行后的完整启动流程,理解结构与功能的关联:
- JVM 读取 MANIFEST.MF
JVM 执行java -jar时,首先找到 JAR 包中的META-INF/MANIFEST.MF,读取Main-Class配置 —— 发现入口类是org.springframework.boot.loader.JarLauncher。 - 启动 JarLauncher 的 main 方法
JVM 调用JarLauncher.main(),此时启动器开始工作:- 初始化
LaunchedURLClassLoader类加载器; - 将
BOOT-INF/classes/封装为URL(格式:jar:file:/xxx/demo.jar!/BOOT-INF/classes/); - 将
BOOT-INF/lib/下的每个依赖 JAR 也封装为URL(格式:jar:file:/xxx/demo.jar!/BOOT-INF/lib/spring-boot.jar!/); - 将这些
URL交给LaunchedURLClassLoader,作为类加载路径。
- 初始化
- 加载并反射调用 Start-Class
JarLauncher读取MANIFEST.MF中的Start-Class(你的主程序类,如DemoApplication):- 通过
LaunchedURLClassLoader加载DemoApplication.class; - 反射调用
DemoApplication.main(String[] args),传入命令行参数。
- 通过
- 启动 Spring Boot 应用
进入你编写的main方法,执行SpringApplication.run(DemoApplication.class, args):- 初始化 Spring 上下文;
- 执行自动配置(加载
application.yml、初始化嵌入式 Tomcat 等); - 启动应用,监听端口,对外提供服务。
与普通 Java JAR 包的核心差异
为了更清晰理解 Spring Boot JAR 的特殊性,我们对比普通 Java JAR(如 Maven jar 插件生成的 JAR):
| 对比维度 | 普通 Java JAR | Spring Boot 可执行 JAR |
|---|---|---|
| 核心结构 | 仅包含自身 .class 文件,无依赖 | 含 BOOT-INF(应用 + 依赖)、启动器工具类 |
| MANIFEST.MF | 无 Main-Class 或仅指向普通类 |
含 Main-Class(JarLauncher)和 Start-Class(应用主类) |
| 类加载器 | 使用 JVM 默认 AppClassLoader |
使用自定义 LaunchedURLClassLoader,支持 BOOT-INF 结构 |
| 执行方式 | 需手动指定类路径:java -cp xxx.jar com.xxx.Main |
直接 java -jar xxx.jar 执行 |
| 依赖管理 | 依赖需外部提供,手动添加到类路径 | 依赖内置在 BOOT-INF/lib,自动加载 |
实战注意事项
不要手动修改 JAR 包结构
BOOT-INF、META-INF 的目录名和位置是固定的,手动修改(如移动 classes 目录)会导致启动器无法找到资源,应用启动失败。依赖冲突排查
若BOOT-INF/lib中存在同名但不同版本的依赖(如spring-core-4.3.13.jar和spring-core-5.3.0.jar),会导致类冲突 —— 可通过mvn dependency:tree分析依赖树,排除冗余依赖。大依赖包的优化
若BOOT-INF/lib中存在超大依赖(如数据库驱动、大数据组件),会导致 JAR 包体积过大 —— 可通过spring-boot-maven-plugin的exclude配置排除这些依赖,部署时通过-Dloader.path外部指定(如java -jar demo.jar -Dloader.path=lib/,将依赖放在外部 lib 目录)。JAR 包解压后的执行
若需解压 JAR 包后执行,可通过以下命令(模拟启动器逻辑):1
2
3
4# 解压 JAR 包
unzip demo.jar -d demo
# 执行启动器,指定类路径和 Start-Class
java -cp "demo/BOOT-INF/classes:demo/BOOT-INF/lib/*" org.springframework.boot.loader.JarLauncher
总结
Spring Boot JAR 包的结构设计是其 “可直接执行” 特性的核心 —— 通过 BOOT-INF 分层管理应用与依赖,通过 MANIFEST.MF 配置启动入口,通过内置启动器和自定义类加载器解决资源加载问题,最终实现 java -jar 一键启动

v1.3.10