Java打包可執(zhí)行JAR文件的三種方式詳解
三種方式打 Java 可執(zhí)行 JAR 包,你用對了嗎
寫完代碼只是開始,如何打包成一個“開箱即用”的 JAR 文件,才是交付的關鍵一步。
很多 Java 開發(fā)者都遇到過這樣的問題: 本地跑得好好的程序,一打包就報錯 ClassNotFoundException? 別慌,這很可能不是你的代碼問題,而是——JAR 包沒打好!
Maven 提供了多種打可執(zhí)行 JAR 的方式。今天我們就來對比三種主流方案:
maven-jar-plugin(輕量外置依賴)maven-assembly-plugin(全家桶打包)maven-shade-plugin(高級防沖突版)
每種方式配真實 pom.xml 配置 + 執(zhí)行命令 + 輸出結構,讓你看完就能上手!
方式一: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 <-啟動類
執(zhí)行命令
java -jar java-demo-1.0-SNAPSHOT.jar
- 優(yōu)點: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 ← 可執(zhí)行 fat jar
└── java-demo-1.0-SNAPSHOT.jar ← 原始包(不可執(zhí)行)
可執(zhí)行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
執(zhí)行命令
java -jar java-demo-jar-with-dependencies.jar
- 優(yōu)點:一個文件搞定,部署簡單
- 缺點:依賴類直接合并,容易因同名類沖突導致運行時錯誤
方式三:maven-shade-plugin—— “高級防沖突版”
不僅能打包所有依賴,還支持 類重定位(relocation) ,從根本上避免類沖突。類重定位其實就是動態(tài)的移動、修改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 ← 可執(zhí)行最終包,把項目中的代碼及所依賴的第三方jar的Class文件都打進去了
└── original-java-demo-1.0-SNAPSHOT.jar ← 原始包(備份),不能直接執(zhí)行,只包含項目中自有的代碼,MENIFEST.MF文件中連Main-Class都沒有。
可執(zhí)行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
執(zhí)行命令
java -jar java-demo-1.0-SNAPSHOT.jar
- 優(yōu)點:支持類重定位、穩(wěn)定性高、被 Spring Boot 等廣泛采用
- 缺點:JAR 體積大,配置略復雜
動態(tài)重命名包示例
如果最終的 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>:原始包內(nèi)不需要重定位的類,類名支持通配符
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 | ? | ? | ?(支持重定位) | 復雜項目、生產(chǎn)環(huán)境 |
到此這篇關于Java打包可執(zhí)行JAR文件的三種方式詳解的文章就介紹到這了,更多相關Java打包JAR內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
聊聊@Autowired注解注入,寫接口名字還是實現(xiàn)類的名字
這篇文章主要介紹了聊聊@Autowired注解注入,寫接口名字還是實現(xiàn)類的名字,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
SpringBoot集成Dubbo啟用gRPC協(xié)議
這篇文章主要介紹了SpringBoot集成Dubbo啟用gRPC協(xié)議,以及與原生 gRPC 在代碼編寫過程中的區(qū)別。感興趣的同學可以參考閱讀2023-04-04

