SpringBoot打包部署解析:jar包的生成和结构
liuian 2025-07-09 14:16 52 浏览
SpringBoot打包部署解析
SpringBoot项目支持两种常见的打包形式: jar 包和 war 包。默认情况下创建的 Spring Boot项目是采用 jar 包形式,如果项目需要 war 包,可通过修改配置打成 war 包。
本章我们将围绕 jar 包和 war 包的运作原理及相关操作进行讲解。
SpringBoo的jar 包
Spring Boot 的 jar 包项目发布形式简单、快捷且内置 web 容器,因此 Spring Boot 将其作为默认选项。在享受便利的同时,我们也需要多少了解一下 Spring Boot 的 jar 包是如何生成的,以及如何通过 jar 包启动运行。本节从 jar 包的生成、结构、运作原理来分析 Spring Boot的实现。
jar包的生成
Spring Boot 的可执行 jar 包又称作 fat jar”,是包含所有三方依赖的 jar。它与传统 jar 包最大的不同是包含了一个 lib 目录和内嵌了 web 容器(以下均以 tomcat 为例)。
jar 包通 常是由集成在 pom.xml 文件中的 maven 插件来生成的。
配置在 pom 文件 build 元素中的 plugins 内。
<build>
<plugins>
<plugin>
<groupId>org . springframework. boot</groupId>
<artifactId>spring- boot-maven- plugin</ artifactId>
</plugin>
</plugins>
</build>
spring-boot-maven-plugin 项目存在于 spring-boot-tools 目录中。spring-boot-maven-plugin默认有 5 个 goals: repackage、 run、 start、 stop、 build-info。在打包的时候默认使用的是 repackage。
spring-boot-maven-plugin 的 repackage 能够将 mvn package 生成的软件包,再次打包为可执行的软件包,并将 mvn package 生成的软件包重命名为*.original。
这就为什么当执行 maven clean package 时,spring-boot-maven-plugin 会在 target 目录下生成两个 jar 文件。
spring - learn-0.0.1- SNAPSHOT. jar
spring - learn-0.0.1- SNAPSHOT . jar . original其中我们可以将 spring-learn-0.0. 1-SNAPSHOTjar.original 文件的后缀 original 去掉,生成的新jar包便是包含业务代码的包(普通的jar包) 。另外的spring-learn-0.0. 1-SNAPSHOTjar包则是在 Spring Boot 中通过 jar jar 启动的包,它包含了应用的依赖,以及 spring boot 相关 class。
spring-boot-maven-plugin 的 repackage 在代码层面调用了 RepackageMojo 的 execute 方法。RepackageMojo 类就是 提供重新打包现有的 jar 或 war 包文件,使得它们可以使用 javajar 来进行启动。
RepackageMojo 的 execute 方法如下。
@Override
public void execute() throws MojoExecut ionException, MojoFailureException
if (this. project . getPackaging() . equals("pom")) {
getLog() . debug("repackage goal could not be applied to pom project.");
return;
if (this.skip) {
getLog() . debug("skipping repackaging as per configuration.");
return;
repackage();
}
在 execute 方法中判断了是否为 pom 项目和是否跳过,如果是,则打印 debug 日志并返回;否则继续执行 repackage 方法。RepackageMojo 中的 repackage 方法相关源代码及操作解析如下。
private void repackage() throws MojoExecutionException {
// maven 生成的 jar, 最终的命名将加上. original 后缀
Artifact source = getSourceArtifact();
//最终为可执行 jar,即 fat jar
File target = getTargetFile();
//获取重新打包器,将 maven 生 成的 jar 重新打包成可执行 jar
Repackager repackager = getRepackager(source . getFile());
//查找并过滤项目运行时依赖的 jar
Set<Artifact> artifacts = filterDependenc ies(this . project. getArtifacts(),
getFilters(getAdditionalFilt
ers()));
//将 artifacts 转换成 L ibraries
Libraries libraries = new ArtifactsLibraries(artifacts, this . requiresUnpa
k,
getLog());ry {
/获得 Spring Boot 启动脚本
LaunchScript launchScript = getLaunchScript();
//执行重新打包,生成 fat jar
repackager . repackage(target, libraries, launchScript);
catch (IOException ex) {
throw new MojoExecut ionException(ex. getMessage(), ex); }
将 maven 生成的 jar 更新成 original 文件
updateArtifact(source, target, repackager . getBackupFile());
}
关于整个 repackage 方法的操作流程在上面代码中已经进行相应注释说明,其基本过程为:获得 maven 生成的普通 jar 包、获得目标 File 对象、获得重新打包器、获得依赖 jar 包、 获得启动脚本,最后通过重新打包器进行重新打包为可通过 java -jar 执行的 jar 包。
其中我们重点看获取 Repackager 的方法 getRepackager 的源代码。
private Repackager getRepackager(File source) {
Repackager repackager = new Repackager(source, this . layoutFactory);
repackager . addMainClassTimeoutWarningL istener(
new LoggingMainClassTimeoutWarningl istener());
//设置 main class 的名称,如果不指定, 则会查找第一个包含 main 方法的类
// repackage 最后将会设置 org. springframework . boot . Loader. JarLauncher
repackager . setMainClass(this . mainClass);
if (this.layout != null) {
getLog(). info("Layout: "+
this. layout);
//比如,layout 返@org. springframework. boot. loader. tools. Layouts . Jar
repackager . setLayout(this . layout. layout());
}
return repackager;
}
getRepackager 方法主要是根据将要被转换的文件(jar 或 war) 创建了 Repackager 对象,并设置启动用的 MainClass 为 org.
springframework.boot.loader.JarLauncher,该配置对应于 jar 包中 Manifest.MF 文件内的 MainClass 值。
同时,如果 layout 不为 null, 通过内部枚举类 L ayoutType 提供的 layout 方法获取对应的重新打包的实现类,比如针对 jar 包的 org.springframework.
boot.loader.tools.Layouts.Jar 类。
枚举类 LayoutType 的定义如下。
public enum LayoutType {
JAR(new Jar()),WAR(new War()),
ZIP(new Expanded()),
DIR(new Expanded()),
NONE(new None());
}
从 LayoutType 的定义可以看出,Spring Boot 其实是支持多种类型的 archive ( 即归档文件) : jar 类型、war 类型、zip 类型、 文件目录类型和 NONE。很显然,使用了相同的实现类来处理 ZIP 文件和 DIR 文件。
jar 类型为 Layouts 类的内部类,可以简单看一下 jar 类型的处理类都包含 了哪些内容。
public static class Jar implements RepackagingLayout {
//获取具体的 L ancher 类全路径
@Override
public String getLauncherClassName() {
return "org. springframework. boot . loader.Jarlauncher";
/获得具体的依赖 jar 包路径
@Override
public String getL ibraryDestination(String libraryName, LibraryScope scop
return "BOOT - INF/lib/";
//获取重新打包的 class 文件路径
@Override
public String getRepackagedClassesLocation() {
return "BOOT -INF/classes/";}
}
通过源代码可以看出,jar 类型的归档文件(jar 包) 中包含了 jar 包启动的 Main-class ( JarLauncher )BOOT-INF/lib/目录和 BOOT-INF/classes/目录。如果看 Expanded 和 None 类,会发现它们又继承自 jar。
最后,我们简单看一下 RepackageMojo 中的 repackage 调用所获取的 Repackager 的repackage 方法。Repackager 中 repackage 方法源码如下。
public void repackage(File destination, Libraries libraries, LaunchScript 1aunch
Script) throws IOException {
//校验目标文件
F (destination == null| | destination. isDirectory())
throw new illegalArgumentException("Invalid destination");
//校验依赖库f (libraries = null) {
throw new IllegalArgumentException("Libraries must not be nu1l");
//校验是否存在对应的 Layout, 如果不存在则创建
if (this.layout == null) {
this. layout = getL ayoutFactory() . getLayout(this . source);
destination = destination. getAbsoluteFile();
File workingSource = this. source;
//检查是否已经重新打包
if (alreadyRepackaged() && this. source.equals(destination)) {
//如果目标文件和 source 相同, 则删除原有备份文件( . original 结尾的), 重新备份 so
urce 文件
if (this. source . equals(destination)) {
workingSource = getBackupFile();
workingSource. delete();
renameFile(this. source, workingSource);
destination. delete();
try {
try (JarFile jarFileSource = new JarFile(workingSource)) {
//核心功能就是创建 JarWriter 向文件指定文件中写入内容
repackage(jarFileSource, destination, libraries, launchScript); }
finally {
if (!this . backupSource && !this. source . equals (workingSource)) {
deleteFile(workingSource);
}
}
}
上述代码的核心业务逻辑如下。
.校验各类参数(文件和路径是否存在)。
.备份待重新打包的文件以.original 结尾, 如果已经存在备份文件则先执行删除操作。
:生成目标文件之前,先清除一下目标文件。
调用重载的 repackage 方法,进行具体(jar 包)文件的生成和 MANIFESTMF 的信息写入。
.最后,判断并执行 workingSource 的清除操作。
用一句话总结上述过程:当符合条件时,对原有 jar 包文件进行备份,并生成新的可以通过 jar-jar 启动的文件。
关于重新打包的 jar 的目录结构及 MANIFEST.MF 文件中的信息,我们将在下一节进行讲解。
jar包的结构
在上一节中,通过 spring-boot-maven-plugin 生 成了可执行的 jar 包,下面分析-下 jar 包
spring-learn-0.0.1-SNAPSHOT.jar的目录结构。
在上述结构中,BOOT-INF/classes 目录中存放业务代码,BOOT-INF/ib 目录中存放了除java 虚拟机之外的所有依赖; org 目 录中存放了 Spring Boot 用来启动 jar 包的相关 class文件; META-INF 目录中存放了 MANIFEST.MF、maven 信息和 spring factories 文件。
其中,Manifest.MF 文件通常被用来定义扩展或档案打包相关数据,它是一个元数据文件,数据格式为名/值对。一个可执行的 jar 文件需要通过该文件来指出该程序的主类。
Manifest-Version: 1.0
Implementation-Title: spring-learn
Implementation-Version: 0. 0.1-SNAPSHOT
Start-Class: com. secbro2. learn. SpringLearnApplication
Spring - Boot-Classes: B0OT-INF/classes/
Spring-Boot-Lib: B0OT-INF/lib/
Build-Jdk-Spec: 1.8
Spring- Boot -Version: 2.2.1. RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org. springframework . boot . loader .Jarlauncher
Manifest.MF 文件中定义 Main-Class 设置为org.
springframework.boot.loader.JarLauncher, 也 就 是 说 , jar 包 程 序 启 动 入 口 为JarL .auncher 类的 main 方法。JarLauncher 类位 于 spring-boot-loader 项目中,在 jar 包的 org 目录中便存储着 Launcher 相关类的 class 文件。
项目的弓导类定义在 Start-Class 属性中,需要注意的是,Start-Class 属性并非 Java 标准的 Manifest.MF 属性。
本文给大家讲解的内容是SpringBoot打包部署解析:jar包的生成和结构
- 下篇文章给大家讲解的是SpringBoot打包部署解析:Launcher实现原理;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!
相关推荐
- 教你把多个视频合并成一个视频的方法
-
一.情况介绍当你有一个m3u8文件和一个目录,目录中有连续的视频片段,这些片段可以连成一段完整的视频。m3u8文件打开后像这样:m3u8文件,可以理解为播放列表,里面是播放视频片段的顺序。视频片段像这...
- 零代码编程:用kimichat合并一个文件夹下的多个文件
-
一个文件夹里面有很多个srt字幕文件,如何借助kimichat来自动批量合并呢?在kimichat对话框中输入提示词:你是一个Python编程专家,完成如下的编程任务:这个文件夹:D:\downloa...
- Java APT_java APT 生成代码
-
JavaAPT(AnnotationProcessingTool)是一种在Java编译阶段处理注解的工具。APT会在编译阶段扫描源代码中的注解,并根据这些注解生成代码、资源文件或其他输出,...
- Unit Runtime:一键运行 AI 生成的代码,或许将成为你的复制 + 粘贴神器
-
在我们构建了UnitMesh架构之后,以及对应的demo之后,便着手于实现UnitMesh架构。于是,我们就继续开始UnitRuntime,以用于直接运行AI生成的代码。PS:...
- 挣脱臃肿的枷锁:为什么说Vert.x是Java开发者手中的一柄利剑?
-
如果你是一名Java开发者,那么你的职业生涯几乎无法避开Spring。它如同一位德高望重的老国王,统治着企业级应用开发的大片疆土。SpringBoot的约定大于配置、SpringCloud的微服务...
- 五年后,谷歌还在全力以赴发展 Kotlin
-
作者|FredericLardinois译者|Sambodhi策划|Tina自2017年谷歌I/O全球开发者大会上,谷歌首次宣布将Kotlin(JetBrains开发的Ja...
- kotlin和java开发哪个好,优缺点对比
-
Kotlin和Java都是常见的编程语言,它们有各自的优缺点。Kotlin的优点:简洁:Kotlin程序相对于Java程序更简洁,可以减少代码量。安全:Kotlin在类型系统和空值安全...
- 移动端架构模式全景解析:从MVC到MVVM,如何选择最佳设计方案?
-
掌握不同架构模式的精髓,是构建可维护、可测试且高效移动应用的关键。在移动应用开发中,选择合适的软件架构模式对项目的可维护性、可测试性和团队协作效率至关重要。随着应用复杂度的增加,一个良好的架构能够帮助...
- 颜值非常高的XShell替代工具Termora,不一样的使用体验!
-
Termora是一款面向开发者和运维人员的跨平台SSH终端与文件管理工具,支持Windows、macOS及Linux系统,通过一体化界面简化远程服务器管理流程。其核心定位是解决多平台环境下远程连接、文...
- 预处理的底层原理和预处理编译运行异常的解决方案
-
若文章对您有帮助,欢迎关注程序员小迷。助您在编程路上越走越好![Mac-10.7.1LionIntel-based]Q:预处理到底干了什么事情?A:预处理,顾名思义,预先做的处理。源代码中...
- 为“架构”再建个模:如何用代码描述软件架构?
-
在架构治理平台ArchGuard中,为了实现对架构的治理,我们需要代码+模型描述所要处理的内容和数据。所以,在ArchGuard中,我们有了代码的模型、依赖的模型、变更的模型等,剩下的两个...
- 深度解析:Google Gemma 3n —— 移动优先的轻量多模态大模型
-
2025年6月,Google正式发布了Gemma3n,这是一款能够在2GB内存环境下运行的轻量级多模态大模型。它延续了Gemma家族的开源基因,同时在架构设计上大幅优化,目标是让...
- 比分网开发技术栈与功能详解_比分网有哪些
-
一、核心功能模块一个基本的比分网通常包含以下模块:首页/总览实时比分看板:滚动展示所有正在进行的比赛,包含比分、比赛时间、红黄牌等关键信息。热门赛事/焦点战:突出显示重要的、关注度高的比赛。赛事导航...
- 设计模式之-生成器_一键生成设计
-
一、【概念定义】——“分步构建复杂对象,隐藏创建细节”生成器模式(BuilderPattern):一种“分步构建型”创建型设计模式,它将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建...
- 构建第一个 Kotlin Android 应用_kotlin简介
-
第一步:安装AndroidStudio(推荐IDE)AndroidStudio是官方推荐的Android开发集成开发环境(IDE),内置对Kotlin的完整支持。1.下载And...
- 一周热门
-
-
【验证码逆向专栏】vaptcha 手势验证码逆向分析
-
Psutil + Flask + Pyecharts + Bootstrap 开发动态可视化系统监控
-
一个解决支持HTML/CSS/JS网页转PDF(高质量)的终极解决方案
-
再见Swagger UI 国人开源了一款超好用的 API 文档生成框架,真香
-
网页转成pdf文件的经验分享 网页转成pdf文件的经验分享怎么弄
-
C++ std::vector 简介
-
飞牛OS入门安装遇到问题,如何解决?
-
系统C盘清理:微信PC端文件清理,扩大C盘可用空间步骤
-
10款高性能NAS丨双十一必看,轻松搞定虚拟机、Docker、软路由
-
python使用fitz模块提取pdf中的图片
-
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)