news 2026/4/15 15:46:20

三种方式打 Java 可执行 JAR 包,你用对了吗?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
三种方式打 Java 可执行 JAR 包,你用对了吗?

三种方式打 Java 可执行 JAR 包,你用对了吗?

写完代码只是开始,如何打包成一个“开箱即用”的 JAR 文件,才是交付的关键一步。

很多 Java 开发者都遇到过这样的问题:本地跑得好好的程序,一打包就报错ClassNotFoundException别慌,这很可能不是你的代码问题,而是——JAR 包没打好!

Maven 提供了多种打可执行 JAR 的方式。今天我们就来对比三种主流方案:

maven-jar-plugin(轻量外置依赖)

maven-assembly-plugin(全家桶打包)

maven-shade-plugin(高级防冲突版)

每种方式配真实pom.xml 配置 + 执行命令 + 输出结构,让你看完就能上手!


方式一:maven-jar-plugin—— “轻量但依赖外置”

它只打包你自己的代码,不包含第三方依赖,但会在MANIFEST.MF中指定依赖路径。

🔧 pom.xml 配置

<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.2.0</version> <configuration> <archive> <manifest> <mainClass>org.example.App</mainClass> <addClasspath>true</addClasspath> <classpathPrefix>dependencies/</classpathPrefix> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.1.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/dependencies/</outputDirectory> <includeScope>runtime</includeScope> </configuration> </execution> </executions> </plugin> </plugins> </build>

📂 打包后结构

📂jar包解压后看长啥样

├── META-INF │ ├── MANIFEST.MF │ └── maven │ └── org.example │ └── java-demo │ ├── pom.properties │ └── pom.xml └── org └── example └── App.class

jar包中的MANIFEST.MF文件

Manifest-Version: 1.0 Created-By: Maven Jar Plugin 3.2.0 Build-Jdk-Spec: 17 Class-Path: dependencies/fastjson2-2.0.60.jar <-第三方依赖包在这里 Main-Class: org.example.App <-启动类

▶️ 执行命令

java -jar java-demo-1.0-SNAPSHOT.jar

✅ 优点:JAR 小、依赖清晰 ❌ 缺点:必须保证dependencies/目录在 JAR 同级,否则运行失败!


📦方式二:maven-assembly-plugin—— “全家桶打包”

生成一个fat jar,把你的代码 + 所有依赖的 class 全部塞进去。

🔧 pom.xml 配置

<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.1.0</version> <configuration> <finalName>java-demo</finalName> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>org.example.App</mainClass> </manifest> </archive> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build>

📂 打包后结构

target/ ├── java-demo-jar-with-dependencies.jar ← 可执行 fat jar └── java-demo-1.0-SNAPSHOT.jar ← 原始包(不可执行)

📂可执行jar包的解压后长啥样?

jar包的MANIFEST.MF文件

Manifest-Version: 1.0 Created-By: Apache Maven 3.9.8 Built-By: Administrator Build-Jdk: 17.0.11 Main-Class: org.example.App

▶️ 执行命令

java -jar java-demo-jar-with-dependencies.jar

✅ 优点:一个文件搞定,部署简单 ❌ 缺点:依赖类直接合并,容易因同名类冲突导致运行时错误


🔒方式三:maven-shade-plugin—— “高级防冲突版”

不仅能打包所有依赖,还支持类重定位(relocation),从根本上避免类冲突。类重定位其实就是动态的移动、修改jar包中的类名、包名。

🔧 pom.xml 配置(基础版)

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <!-- 此处按需编写更具体的配置 --> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>org.example.App</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin>

💡 如需解决冲突,可加<relocations>重命名包路径(此处略,按需扩展)。

📂 打包后结构

target/ ├── java-demo-1.0-SNAPSHOT.jar ← 可执行最终包,把项目中的代码及所依赖的第三方jar的Class文件都打进去了 └── original-java-demo-1.0-SNAPSHOT.jar ← 原始包(备份),不能直接执行,只包含项目中自有的代码,MENIFEST.MF文件中连Main-Class都没有。

📂可执行jar包的解压后长啥样?

jar包的MANIFEST.MF文件

Manifest-Version: 1.0 Created-By: Maven JAR Plugin 3.4.1 Build-Jdk-Spec: 17 Main-Class: org.example.App

▶️ 执行命令

java -jar java-demo-1.0-SNAPSHOT.jar

✅ 优点:支持类重定位、稳定性高、被 Spring Boot 等广泛采用 ❌ 缺点:JAR 体积大,配置略复杂


动态重命名包示例

如果最终的 jar 包被其他的项目所依赖的话, classpath 中可能存在重复的 class 文件,直接地引用此 jar 包中的类可能会导致类加载冲突。为了解决这个问题,我们可以使用 shade 提供的重定位功能,把部分类移动到一个全新的包中。

<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>org.example.App</mainClass> </transformer> </transformers> <relocations> <relocation> <!--在打包时,将项目中的原始包com.alibaba.fastjson2,重命名为com.asia.fastjson2--> <pattern>com.alibaba.fastjson2</pattern> <shadedPattern>com.asia.fastjson2</shadedPattern> <!--排除以下类路径,保持不动--> <excludes> <exclude>com.alibaba.fastjson2.JSON</exclude> <exclude>com.alibaba.fastjson2.filter.*</exclude> </excludes> </relocation> </relocations> </configuration> </execution> </executions> </plugin> </plugins> </build>

涉及标签:

  • <pattern>:原始包名
  • <shadedPattern>:重命名后的包名
  • <excludes>:原始包内不需要重定位的类,类名支持通配符
jar包解压后看什么样?

. ├── com │ ├── alibaba │ │ └── fastjson2 │ │ ├── filter │ │ └── JSON.class │ └── asia │ └── fastjson2 │ ├── annotation │ ├── codec │ ├── function │ ├── internal │ ├── 省略。。。 │ ├── TypeReference.class │ ├── util │ └── writer ├── META-INF │ ├── MANIFEST.MF │ ├── maven │ │ ├── com.alibaba.fastjson2 │ │ │ └── fastjson2 │ │ └── org.example │ │ └── java-demo │ ├── native-image │ │ └── com.alibaba.fastjson2 │ │ └── fastjson2 │ ├── proguard │ │ └── fastjson2.pro │ ├── scm │ │ └── com.alibaba.fastjson2 │ │ └── fastjson2 │ └── versions │ └── 9 │ └── module-info.class ├── org │ └── example │ └── App.class └── schema ├── 2020-12.json └── draft-04.json

🎯 一张表看懂怎么选

插件是否含依赖是否可直接运行能否防冲突推荐场景
maven-jar-plugin❌(外置)✅(需依赖目录)轻量部署、依赖独立管理
maven-assembly-plugin快速打包、简单项目
maven-shade-plugin✅(支持重定位)复杂项目、生产环境

标签:

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 9:35:18

05_C 语言进阶之避坑指南:编译器优化等级 —— 嵌入式开发中被忽略的 “隐形陷阱”

C 语言进阶之避坑指南:编译器优化等级 —— 嵌入式开发中被忽略的 “隐形陷阱” 一、编译器优化等级的 “坑”,你踩过吗? “代码在 O0 调试模式下运行正常,切换到 O2 优化后直接卡死?” “全局变量在优化后被编译器‘吃掉’,中断中修改的值主循环读不到?” “调试时…

作者头像 李华
网站建设 2026/4/16 5:57:56

别把数组当“流水账”:用 Summary Ranges 教你学会“结构化思考”

别把数组当“流水账”:用 Summary Ranges 教你学会“结构化思考” 作者:Echo_Wish 一、引子:我们处理的不是数组,是“信息结构化” 咱们聊一个特别小、但特别有味道的题目:汇总区间(Summary Ranges)。 这个题我第一次看到时,直觉觉得:“不就处理个数组嘛,这能有啥…

作者头像 李华
网站建设 2026/4/15 16:32:06

基础进制转换

m 进制转 n 进制&#xff08;通用实现&#xff09;m 进制转 n 进制的核心逻辑是以 10 进制为中间桥梁&#xff1a;先将 m 进制数转为 10 进制&#xff0c;再将 10 进制数转为 n 进制。该方法适配任意合法进制&#xff08;2 ≤ m,n ≤ 36&#xff09;&#xff0c;兼容数字 字母…

作者头像 李华
网站建设 2026/4/16 7:33:09

node基础

从node文档里抄了一些东西贴出来&#xff0c;省的每次都找好久&#xff08;node文档的那个目录&#xff0c;我感觉找东西挺费劲的&#xff09; &#xff08;ps:为什么node v25版本的文档里找不到path模块&#xff0c;v24版本的文档里找到了&#xff01;&#xff01;&#xff01…

作者头像 李华