java?-jar啟動原理詳解(附實操驗證和注意事項)
前言
java -jar 是 Java 中運行可執(zhí)行 JAR 包(Executable JAR)的核心命令,其底層依托 JVM 類加載機制、JAR 包規(guī)范和程序入口約定實現(xiàn),核心邏輯是「JVM 解析 JAR 包元數(shù)據(jù)→加載指定主類→執(zhí)行入口方法」,以下從核心前提、完整啟動流程、關鍵細節(jié)、與普通啟動的區(qū)別四方面徹底講清原理,附實操驗證和注意事項。
一、核心前提:可執(zhí)行 JAR 包的特殊要求
java -jar 能啟動的前提是JAR 包必須是「可執(zhí)行 JAR」,普通依賴 JAR 包(僅含 class 文件,無啟動配置)無法通過該命令運行??蓤?zhí)行 JAR 包的核心標識是:在 META-INF/MANIFEST.MF 文件中聲明主類入口,這是 JVM 識別「啟動類」的唯一依據(jù)。
1. 可執(zhí)行 JAR 包的MANIFEST.MF核心配置
該文件是 JAR 包的「元數(shù)據(jù)清單」,java -jar 啟動時會優(yōu)先讀取其中的 Main-Class 屬性,格式要求嚴格(冒號后必須有 1 個空格,結尾無多余空行),示例:
manifest
Manifest-Version: 1.0 Main-Class: com.example.DemoApplication # 核心:指定程序入口主類(含全類名) Class-Path: lib/commons-lang3-3.14.0.jar lib/fastjson2-2.0.32.jar # 可選:依賴的外部JAR包路徑
Main-Class:必填,指定包含public static void main(String[] args)方法的主類(全類名,無.class后綴);Class-Path:可選,聲明當前 JAR 包依賴的外部 JAR 包(相對路徑,多個依賴用空格分隔,JVM 會按此加載依賴類);- 格式要求:屬性名大小寫固定,冒號
:后必須跟1 個空格,否則 JVM 會解析失敗。
2. 普通 JAR 與可執(zhí)行 JAR 的核心區(qū)別
| 類型 | 核心特征 | 啟動方式 |
|---|---|---|
| 普通依賴 JAR | 僅含 class 文件,無 Main-Class | 無法 java -jar,僅作為其他項目的依賴被類加載 |
| 可執(zhí)行 JAR | 含 Main-Class 聲明,有主入口 | 直接 java -jar xxx.jar 啟動 |
二、java -jar xxx.jar完整啟動流程(JVM 視角)
當在命令行執(zhí)行 java -jar 命令時,JVM 會按固定步驟完成從「解析 JAR 包」到「執(zhí)行 main 方法」的全過程,無額外手動干預,全程自動化,步驟如下(按執(zhí)行順序):
步驟 1:JVM 解析命令行參數(shù),識別-jar標識
JVM 啟動器(java.exe/java 可執(zhí)行文件)首先解析命令行參數(shù),當檢測到 -jar 標識時,會觸發(fā) **「JAR 包啟動模式」**,后續(xù)邏輯均圍繞「加載并運行可執(zhí)行 JAR 包」展開,同時忽略命令行中后續(xù)的其他類名參數(shù)(僅以 JAR 包為核心)。
步驟 2:加載并解析 JAR 包中的META-INF/MANIFEST.MF文件
JVM 會打開指定的 JAR 包(如 xxx.jar),按規(guī)范讀取其中 META-INF 目錄下的 MANIFEST.MF 元數(shù)據(jù)文件,核心做 2 件事:
- 校驗文件格式是否合法(如是否有
Manifest-Version基礎屬性); - 提取 **
Main-Class屬性值 **(主類全類名),若未找到該屬性,直接拋出異常:no main manifest attribute, in xxx.jar(最常見啟動失敗原因)。
步驟 3:基于 JAR 包創(chuàng)建專屬的「JAR 類加載器(JarClassLoader)」
Java 類加載遵循「雙親委派模型」,但 java -jar 啟動時,JVM 會創(chuàng)建專屬的 JarClassLoader(繼承自 URLClassLoader),該類加載器的核心作用是:
- 以整個 JAR 包為「類加載源」,負責從 JAR 包的壓縮結構中加載所有 class 文件(包括主類、業(yè)務類、內部依賴類);
- 若
MANIFEST.MF中有Class-Path屬性,JarClassLoader 會同時將該屬性聲明的外部 JAR 包加入類加載路徑,按順序加載外部依賴類。
核心特點:JarClassLoader 僅加載當前 JAR 包及
Class-Path聲明的依賴,與系統(tǒng)類加載器(AppClassLoader)隔離,保證啟動環(huán)境的獨立性。
步驟 4:通過 JarClassLoader 加載Main-Class指定的主類
JarClassLoader 會根據(jù) Main-Class 的全類名,在 JAR 包中找到對應的 class 文件(如 com/example/DemoApplication.class,JAR 包中類的路徑與包結構一致),并完成類的加載、鏈接、初始化(類加載的三個階段):
- 加載:將 class 文件的二進制數(shù)據(jù)讀入內存,生成 Class 對象;
- 鏈接:驗證(校驗 class 文件合法性)→ 準備(為類變量分配內存)→ 解析(將符號引用轉為直接引用);
- 初始化:執(zhí)行類的靜態(tài)代碼塊、初始化靜態(tài)變量,完成類的初始化。
若主類加載失敗(如類不存在、依賴缺失、class 文件損壞),會拋出
ClassNotFoundException或NoClassDefFoundError異常,啟動終止。
步驟 5:反射調用主類的public static void main(String[] args)方法
主類加載完成后,JVM 會通過Java 反射機制定位主類中的 main 方法,核心要求是該方法必須滿足固定簽名:
// 必須是 public + static + void,參數(shù)為 String[],方法名嚴格為 main(大小寫敏感) public static void main(String[] args)
JVM 會將命令行中 JAR 包后的參數(shù)(如 java -jar xxx.jar arg1 arg2)封裝為 String[] 數(shù)組,傳遞給 main 方法并執(zhí)行該方法,此時程序正式啟動,進入業(yè)務邏輯執(zhí)行階段。
步驟 6:程序運行與 JVM 生命周期綁定
main 方法是程序的「入口主線程」,JVM 的生命周期會與該主線程綁定:
- 若主線程執(zhí)行完畢(無后臺線程 / 守護線程),JVM 會正常退出;
- 若主線程啟動了其他用戶線程(如業(yè)務線程、定時任務),JVM 會等待所有用戶線程執(zhí)行完畢后再退出;
- 若存在守護線程(如垃圾回收線程),守護線程會隨 JVM 退出而終止。
三、關鍵細節(jié)與常見問題解析
1. 最常見啟動失敗:no main manifest attribute, in xxx.jar
- 原因:JAR 包不是可執(zhí)行 JAR,
MANIFEST.MF中缺失Main-Class屬性,或屬性格式錯誤(如冒號后無空格); - 解決:重新打包為可執(zhí)行 JAR,確保
MANIFEST.MF中正確聲明Main-Class(IDEA/Maven/Gradle 打包時可自動配置)。
2.Class-Path屬性的使用注意事項
- 路徑類型:僅支持相對路徑(相對當前 JAR 包的運行目錄),不支持絕對路徑;
- 分隔符:多個依賴用空格分隔(不是逗號),示例:
Class-Path: lib/a.jar lib/b.jar; - 加載規(guī)則:JarClassLoader 會按
Class-Path順序加載外部依賴,若依賴缺失,會拋出ClassNotFoundException。
3.java -jar與普通java 主類名啟動的核心區(qū)別
很多人會混淆兩種啟動方式,核心差異在類加載器和類加載路徑,具體對比:
| 啟動方式 | 類加載器 | 類加載路徑 | 適用場景 |
|---|---|---|---|
java -jar xxx.jar | JarClassLoader | 僅 JAR 包內 + Class-Path | 生產環(huán)境獨立運行可執(zhí)行程序 |
java com.example.Main | AppClassLoader | 系統(tǒng)類路徑(CLASSPATH) | 開發(fā)環(huán)境快速運行,依賴已在 CLASSPATH 中 |
關鍵:
java -jar啟動時,系統(tǒng)環(huán)境變量 CLASSPATH 會被忽略,所有依賴必須通過 JAR 包內或Class-Path聲明,保證程序運行不依賴外部環(huán)境配置。
4. 命令行參數(shù)傳遞:java -jar xxx.jar arg1 arg2
- 傳遞規(guī)則:JAR 包后的所有參數(shù)會被封裝為
String[],傳遞給主類的main方法; - 示例:執(zhí)行
java -jar demo.jar 8080 dev,則main方法的args數(shù)組為{"8080", "dev"}; - 注意:
-jar前的參數(shù)是 JVM 參數(shù)(如-Xmx512m、-Dspring.profiles.active=dev),后為程序參數(shù),示例:
# -Xmx512m:JVM參數(shù)(最大堆內存);-Dxxx:系統(tǒng)屬性;8080:程序參數(shù) java -Xmx512m -Dspring.profiles.active=dev -jar demo.jar 8080
四、實操驗證:手動查看可執(zhí)行 JAR 包的MANIFEST.MF
可通過解壓工具(如 WinRAR/7-Zip)直接打開 JAR 包,進入 META-INF 目錄,用記事本打開 MANIFEST.MF 文件,驗證是否有 Main-Class 屬性,步驟:
- 找到可執(zhí)行 JAR 包(如
demo.jar),右鍵選擇「解壓到當前文件夾」; - 進入解壓后的
META-INF目錄,打開MANIFEST.MF; - 檢查是否有
Main-Class: 全類名配置,格式是否正確(冒號后有空格)。
五、擴展:Maven/Gradle 打包可執(zhí)行 JAR 包的原理
日常開發(fā)中,我們不會手動編寫 MANIFEST.MF,而是通過 Maven(maven-jar-plugin/spring-boot-maven-plugin)或 Gradle 插件自動打包,核心原理是:
- 插件在打包時,會根據(jù)配置的「主類全類名」,自動生成
MANIFEST.MF文件并寫入Main-Class屬性; - Spring Boot 可執(zhí)行 JAR 包特殊:會生成嵌套 JAR 結構,同時聲明
Spring-Boot-Main-Class和Main-Class(Main-Class為 Spring Boot 啟動器org.springframework.boot.loader.JarLauncher,再由其加載實際業(yè)務主類)。
六、核心總結
java -jar啟動的前提是 JAR 包為「可執(zhí)行 JAR」,核心標識是META-INF/MANIFEST.MF中聲明Main-Class;- 完整啟動流程:解析 -jar 標識→讀取 MANIFEST.MF→創(chuàng)建 JarClassLoader→加載主類→反射執(zhí)行 main 方法;
- JarClassLoader 是專屬類加載器,僅加載 JAR 包內及
Class-Path聲明的依賴,忽略系統(tǒng) CLASSPATH; - 最常見失敗原因是缺失
Main-Class或格式錯誤,解決方式是重新打包并正確配置主類; -jar前為 JVM 參數(shù)(如-Xmx、-D),后為程序參數(shù),會傳遞給main方法的args數(shù)組。
到此這篇關于java -jar啟動原理詳解的文章就介紹到這了,更多相關java -jar啟動原理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java在PowerPoint中添加上標和下標的實現(xiàn)方法
當我們在演示文稿中添加商標、版權或其他符號時,我們可能希望該符號出現(xiàn)在某個文本的上方或下方。在Microsoft PowerPoint中,我們可以通過對符號應用上標或下標格式來實現(xiàn)這種效果,這篇文章主要介紹了Java在PowerPoint中添加上標和下標,需要的朋友可以參考下2022-10-10
淺談關于Java中TimeZone鎖競爭引發(fā)的問題解決
本文主要介紹了淺談關于Java中TimeZone鎖競爭引發(fā)的問題解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2026-02-02
java.lang.NoClassDefFoundError錯誤的原因及解決方法
這篇文章主要給大家介紹了關于java.lang.NoClassDefFoundError錯誤的原因及解決的相關資料,java.lang.NoClassDefFoundError是Java虛擬機在運行時無法找到特定類的錯誤,需要的朋友可以參考下2023-10-10
Java list與set中contains()方法效率案例詳解
這篇文章主要介紹了Java list與set中contains()方法效率案例詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下2021-08-08
減小Maven項目生成的JAR包體積實現(xiàn)提升運維效率
在Maven構建Java項目過程中,減小JAR包體積可通過排除不必要的依賴和使依賴jar包獨立于應用jar包來實現(xiàn),在pom.xml文件中使用<exclusions>標簽排除不需要的依賴,有助于顯著降低JAR包大小,此外,將依賴打包到應用外,可減少應用包的體積2024-10-10
解決IDEA?JDK9沒有module-info.java的問題
這篇文章主要介紹了解決IDEA?JDK9沒有module-info.java的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
解決Java壓縮zip異常java.util.zip.ZipException:duplicate entry
這篇文章主要介紹了解決Java壓縮zip異常java.util.zip.ZipException:duplicate entry:問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12

