0%

SpringBoot打成jar包的结构

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
demo-0.0.1-SNAPSHOT.jar  # Spring Boot 可执行 JAR
├─ BOOT-INF/ # 核心目录:存放应用代码和依赖
│ ├─ classes/ # 1. 应用编译后的 .class 文件和配置
│ │ ├─ com/ # 项目包结构(如 com.zhanghe.study.springboot)
│ │ │ └─ DemoApplication.class # 你编写的主程序类(含 main 方法)
│ │ ├─ application.yml # 应用配置文件(自定义配置)
│ │ └─ static/ # 静态资源(CSS/JS/图片,若存在)
│ └─ lib/ # 2. 项目依赖的第三方 JAR 库
│ ├─ spring-boot-1.5.9.RELEASE.jar # Spring Boot 核心依赖
│ ├─ spring-webmvc-4.3.13.RELEASE.jar # Web 依赖
│ ├─ mybatis-3.4.6.jar # 自定义依赖(如 MyBatis)
│ └─ ... # 其他所有依赖(Maven 引入的依赖都会打包到这里)
├─ META-INF/ # 3. 元信息目录:JAR 清单和依赖索引
│ ├─ MANIFEST.MF # 关键:JAR 清单文件(启动配置核心)
│ └─ maven/ # Maven 构建信息(可选,如 pom.xml 快照)
│ └─ com.zhanghe.study/demo/
│ ├─ pom.xml # 项目的 pom.xml 副本
│ └─ pom.properties # Maven 构建属性(如版本、groupId)
└─ org/ # 4. Spring Boot 启动器目录:内置启动工具类
└─ springframework/
└─ boot/
└─ loader/ # 启动器核心包
├─ JarLauncher.class # JAR 包启动器(Main-Class 指向此类)
├─ WarLauncher.class # WAR 包启动器(若打包为 WAR 则用此类)
└─ LaunchedURLClassLoader.class # 自定义类加载器(加载 BOOT-INF 资源)

这个结构的核心是 “分层存放”:将 “应用代码”“依赖库”“启动工具” 分离,既保证了可执行性,又便于管理资源。

核心目录详解:每个目录的作用

1. BOOT-INF/:应用与依赖的核心存放地

BOOT-INF 是 Spring Boot JAR 包的 “心脏”,所有与项目业务相关的内容都在这里,分为 classeslib 两个子目录。

(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.jartomcat-embed-core.jar 等依赖;若引入了 mybatis-spring-boot-starter,则会包含 mybatis.jarmybatis-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,启动 JarLauncherJarLauncher 再根据 Spring-Boot-ClassesSpring-Boot-Lib 找到应用代码和依赖,最后通过反射调用 Start-Class(你的主程序类)的 main 方法,启动 Spring Boot 应用。

(2)META-INF/maven/:Maven 构建信息(可选)
  • 内容:项目的 pom.xml 副本和 pom.properties(包含 groupIdartifactIdversion 等构建属性)。
  • 作用:仅用于记录构建信息,不影响应用启动 —— 若需查看 JAR 包对应的源码版本或依赖,可解压后查看此目录下的 pom.xml

3. org/springframework/boot/loader/:Spring Boot 启动器工具类

这是 Spring Boot 内置的 “启动工具包”,包含了让 JAR 包可执行的核心逻辑,关键类有三个:

(1)JarLauncher.class:JAR 包的启动器
  • 作用:Main-Class指向的类,是 JVM 执行的入口 —— 它的核心任务是:
    1. 初始化自定义类加载器 LaunchedURLClassLoader
    2. 告诉类加载器从 BOOT-INF/classes/ 加载应用类,从 BOOT-INF/lib/ 加载依赖类;
    3. 反射调用 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 执行后的完整启动流程,理解结构与功能的关联:

  1. JVM 读取 MANIFEST.MF
    JVM 执行 java -jar 时,首先找到 JAR 包中的 META-INF/MANIFEST.MF,读取 Main-Class 配置 —— 发现入口类是 org.springframework.boot.loader.JarLauncher
  2. 启动 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,作为类加载路径。
  3. 加载并反射调用 Start-Class
    JarLauncher 读取 MANIFEST.MF 中的 Start-Class(你的主程序类,如 DemoApplication):
    • 通过 LaunchedURLClassLoader 加载 DemoApplication.class
    • 反射调用 DemoApplication.main(String[] args),传入命令行参数。
  4. 启动 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,自动加载

实战注意事项

  1. 不要手动修改 JAR 包结构
    BOOT-INF、META-INF 的目录名和位置是固定的,手动修改(如移动 classes 目录)会导致启动器无法找到资源,应用启动失败。

  2. 依赖冲突排查
    BOOT-INF/lib 中存在同名但不同版本的依赖(如 spring-core-4.3.13.jarspring-core-5.3.0.jar),会导致类冲突 —— 可通过 mvn dependency:tree 分析依赖树,排除冗余依赖。

  3. 大依赖包的优化
    BOOT-INF/lib 中存在超大依赖(如数据库驱动、大数据组件),会导致 JAR 包体积过大 —— 可通过 spring-boot-maven-pluginexclude 配置排除这些依赖,部署时通过 -Dloader.path 外部指定(如 java -jar demo.jar -Dloader.path=lib/,将依赖放在外部 lib 目录)。

  4. 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 一键启动

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10