当前位置: 首页 > news >正文

三种方式打 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✅(支持重定位)复杂项目、生产环境

标签:

http://www.cnnetsun.cn/news/27340.html

相关文章:

  • Airflow 做 ETL,真不是“排个 DAG 就完事儿”:那些年我踩过的坑与悟出的道
  • 数据库连接池监控最佳实践:用 Prometheus + Grafana 打造可视化监控体系
  • Windows验机
  • 别让孩子视力提早“透支” ,这份护眼指南请收好
  • 儿童青少年近视干预科学指引,破解家长近视防控焦虑
  • 解析 .NET 核心基石:CTS、CLS 与 CLR 的核心价值与协同作用
  • Selinux权限的检测
  • 常见报错org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): org.example.dem
  • 甲骨文AI投资支出激增致股价创24年最大跌幅
  • TinyMCE4粘贴word超链接自动解析域名
  • TinyMCE6处理微信公众号音频视频嵌入
  • 昇腾 Ascend 自定义算子开发全攻略:从 TBE DSL 到 AICPU,打通 AI 加速最后一公里
  • 当电机开始“唱歌“:NVH工程师的降噪日常
  • AI界的“经济适用男“!80亿参数小模型完胜GPT-5,成本降低70%,CSDN程序员必藏的智能调度方案
  • FPGA教程系列-Vivado Aurora 8B/10B 例程解读
  • 227827827
  • MCU的启动流程你了解么?
  • 逻辑回归(Logistic Regression)进行多分类的实战
  • RNN(循环神经网络)原理
  • 人机协同重构创作生态——生成式AI赋能内容产业的变革与思考
  • Java 小白求职者在互联网大厂的面试实录:从 Spring Boot 到微服务架构
  • V助手舆情分析智能体:重塑舆情分析,从“人找信息”到“信息为人”
  • 连接2026:十款远程控制软件真实力横评与选择指南
  • 计算机毕业设计springboot基于Spark++Vue.js的学生管理系统 Spark+Vue 高校学生综合信息管理平台 基于 SpringBoot+Spark+Vue 的全链路学生事务中心
  • JavaScript 集合操作的哈希碰撞:攻击者如何利用特殊 Key 导致 Map/Set 性能降级到 O(N)
  • 为什么 C盘空间会莫名其妙减少(即使没装新软件)?
  • 17、深入理解 Linux 文件系统机制与结构
  • 29、Linux 软件使用与故障排除指南
  • 从入门到转行:网络安全自学与跳槽的终极建议
  • 网络安全小白自学之路,别拜师了,求人不如求己_网络安全小白怎么自学