news 2026/3/1 10:40:06

java -jar 启动原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
java -jar 启动原理

java -jar是 Java 中运行可执行 JAR 包(Executable JAR)的核心命令,其底层依托 JVM 类加载机制、JAR 包规范和程序入口约定实现,核心逻辑是「JVM 解析 JAR 包元数据→加载指定主类→执行入口方法」,以下从核心前提、完整启动流程、关键细节、与普通启动的区别四方面彻底讲清原理,附实操验证和注意事项。

一、核心前提:可执行 JAR 包的特殊要求

java -jar能启动的前提是JAR 包必须是「可执行 JAR」,普通依赖 JAR 包(仅含 class 文件,无启动配置)无法通过该命令运行。可执行 JAR 包的核心标识是:META-INF/MANIFEST.MF文件中声明主类入口,这是 JVM 识别「启动类」的唯一依据。

1. 可执行 JAR 包的MANIFEST.MF核心配置

该文件是 JAR 包的「元数据清单」,java -jar启动时会优先读取其中的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 与可执行 JAR 的核心区别

类型核心特征启动方式
普通依赖 JAR仅含 class 文件,无Main-Class无法java -jar,仅作为其他项目的依赖被类加载
可执行 JARMain-Class声明,有主入口直接java -jar xxx.jar启动

二、java -jar xxx.jar完整启动流程(JVM 视角)

当在命令行执行java -jar命令时,JVM 会按固定步骤完成从「解析 JAR 包」到「执行 main 方法」的全过程,无额外手动干预,全程自动化,步骤如下(按执行顺序):

步骤 1:JVM 解析命令行参数,识别-jar标识

JVM 启动器(java.exe/java可执行文件)首先解析命令行参数,当检测到-jar标识时,会触发 **「JAR 包启动模式」**,后续逻辑均围绕「加载并运行可执行 JAR 包」展开,同时忽略命令行中后续的其他类名参数(仅以 JAR 包为核心)。

步骤 2:加载并解析 JAR 包中的META-INF/MANIFEST.MF文件

JVM 会打开指定的 JAR 包(如xxx.jar),按规范读取其中META-INF目录下的MANIFEST.MF元数据文件,核心做 2 件事:

  1. 校验文件格式是否合法(如是否有Manifest-Version基础属性);
  2. 提取 **Main-Class属性值 **(主类全类名),若未找到该属性,直接抛出异常:no main manifest attribute, in xxx.jar(最常见启动失败原因)。

步骤 3:基于 JAR 包创建专属的「JAR 类加载器(JarClassLoader)」

Java 类加载遵循「双亲委派模型」,但java -jar启动时,JVM 会创建专属的 JarClassLoader(继承自 URLClassLoader),该类加载器的核心作用是:

  • 以整个 JAR 包为「类加载源」,负责从 JAR 包的压缩结构中加载所有 class 文件(包括主类、业务类、内部依赖类);
  • MANIFEST.MF中有Class-Path属性,JarClassLoader 会同时将该属性声明的外部 JAR 包加入类加载路径,按顺序加载外部依赖类。

核心特点:JarClassLoader 仅加载当前 JAR 包及Class-Path声明的依赖,与系统类加载器(AppClassLoader)隔离,保证启动环境的独立性。

步骤 4:通过 JarClassLoader 加载Main-Class指定的主类

JarClassLoader 会根据Main-Class的全类名,在 JAR 包中找到对应的 class 文件(如com/example/DemoApplication.class,JAR 包中类的路径与包结构一致),并完成类的加载、链接、初始化(类加载的三个阶段):

  1. 加载:将 class 文件的二进制数据读入内存,生成 Class 对象;
  2. 链接:验证(校验 class 文件合法性)→ 准备(为类变量分配内存)→ 解析(将符号引用转为直接引用);
  3. 初始化:执行类的静态代码块、初始化静态变量,完成类的初始化。

若主类加载失败(如类不存在、依赖缺失、class 文件损坏),会抛出ClassNotFoundExceptionNoClassDefFoundError异常,启动终止。

步骤 5:反射调用主类的public static void main(String[] args)方法

主类加载完成后,JVM 会通过Java 反射机制定位主类中的main方法,核心要求是该方法必须满足固定签名

java

运行

// 必须是 public + static + void,参数为 String[],方法名严格为 main(大小写敏感) public static void main(String[] args)

JVM 会将命令行中 JAR 包后的参数(如java -jar xxx.jar arg1 arg2)封装为String[]数组,传递给main方法并执行该方法,此时程序正式启动,进入业务逻辑执行阶段。

步骤 6:程序运行与 JVM 生命周期绑定

main方法是程序的「入口主线程」,JVM 的生命周期会与该主线程绑定:

  • 若主线程执行完毕(无后台线程 / 守护线程),JVM 会正常退出;
  • 若主线程启动了其他用户线程(如业务线程、定时任务),JVM 会等待所有用户线程执行完毕后再退出;
  • 若存在守护线程(如垃圾回收线程),守护线程会随 JVM 退出而终止。

三、关键细节与常见问题解析

1. 最常见启动失败:no main manifest attribute, in xxx.jar

  • 原因:JAR 包不是可执行 JAR,MANIFEST.MF中缺失Main-Class属性,或属性格式错误(如冒号后无空格);
  • 解决:重新打包为可执行 JAR,确保MANIFEST.MF中正确声明Main-Class(IDEA/Maven/Gradle 打包时可自动配置)。

2.Class-Path属性的使用注意事项

  • 路径类型:仅支持相对路径(相对当前 JAR 包的运行目录),不支持绝对路径;
  • 分隔符:多个依赖用空格分隔(不是逗号),示例:Class-Path: lib/a.jar lib/b.jar
  • 加载规则:JarClassLoader 会按Class-Path顺序加载外部依赖,若依赖缺失,会抛出ClassNotFoundException

3.java -jar与普通java 主类名启动的核心区别

很多人会混淆两种启动方式,核心差异在类加载器和类加载路径,具体对比:

启动方式类加载器类加载路径适用场景
java -jar xxx.jarJarClassLoader仅 JAR 包内 +Class-Path生产环境独立运行可执行程序
java com.example.MainAppClassLoader系统类路径(CLASSPATH)开发环境快速运行,依赖已在 CLASSPATH 中

关键:java -jar启动时,系统环境变量 CLASSPATH 会被忽略,所有依赖必须通过 JAR 包内或Class-Path声明,保证程序运行不依赖外部环境配置。

4. 命令行参数传递:java -jar xxx.jar arg1 arg2

  • 传递规则:JAR 包后的所有参数会被封装为String[],传递给主类的main方法;
  • 示例:执行java -jar demo.jar 8080 dev,则main方法的args数组为{"8080", "dev"}
  • 注意:-jar前的参数是 JVM 参数(如-Xmx512m-Dspring.profiles.active=dev),后为程序参数,示例:

    bash

    运行

    # -Xmx512m:JVM参数(最大堆内存);-Dxxx:系统属性;8080:程序参数 java -Xmx512m -Dspring.profiles.active=dev -jar demo.jar 8080

四、实操验证:手动查看可执行 JAR 包的MANIFEST.MF

可通过解压工具(如 WinRAR/7-Zip)直接打开 JAR 包,进入META-INF目录,用记事本打开MANIFEST.MF文件,验证是否有Main-Class属性,步骤:

  1. 找到可执行 JAR 包(如demo.jar),右键选择「解压到当前文件夹」;
  2. 进入解压后的META-INF目录,打开MANIFEST.MF
  3. 检查是否有Main-Class: 全类名配置,格式是否正确(冒号后有空格)。

五、扩展:Maven/Gradle 打包可执行 JAR 包的原理

日常开发中,我们不会手动编写MANIFEST.MF,而是通过 Maven(maven-jar-plugin/spring-boot-maven-plugin)或 Gradle 插件自动打包,核心原理是:

  • 插件在打包时,会根据配置的「主类全类名」,自动生成MANIFEST.MF文件并写入Main-Class属性;
  • Spring Boot 可执行 JAR 包特殊:会生成嵌套 JAR 结构,同时声明Spring-Boot-Main-ClassMain-ClassMain-Class为 Spring Boot 启动器org.springframework.boot.loader.JarLauncher,再由其加载实际业务主类)。

六、核心总结

  1. java -jar启动的前提是 JAR 包为「可执行 JAR」,核心标识是META-INF/MANIFEST.MF中声明Main-Class
  2. 完整启动流程:解析-jar标识→读取 MANIFEST.MF→创建 JarClassLoader→加载主类→反射执行 main 方法
  3. JarClassLoader 是专属类加载器,仅加载 JAR 包内及Class-Path声明的依赖,忽略系统 CLASSPATH
  4. 最常见失败原因是缺失Main-Class或格式错误,解决方式是重新打包并正确配置主类;
  5. -jar前为 JVM 参数(如-Xmx-D),后为程序参数,会传递给main方法的args数组。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/25 17:15:48

Chandra OCR应用场景:科研实验室实验记录PDF→结构化时间序列数据提取

Chandra OCR应用场景:科研实验室实验记录PDF→结构化时间序列数据提取 1. 为什么科研人员需要Chandra OCR? 在高校和工业界实验室里,每天都有大量手写打印混合的实验记录本被扫描成PDF存档——温度曲线手绘图旁是铅笔标注的采样时间&#x…

作者头像 李华
网站建设 2026/2/16 21:16:32

gpt-oss-20b推理等级设置技巧,不同场景灵活切换

gpt-oss-20b推理等级设置技巧,不同场景灵活切换 1. 为什么推理等级不是“开关”,而是“调音旋钮” 你可能已经试过在gpt-oss-20b-WEBUI里点开下拉菜单,看到Low / Medium / High三个选项,随手选一个就开聊——结果发现&#xff1…

作者头像 李华
网站建设 2026/3/1 3:25:22

Qwen2.5-1.5B惊艳效果:技术博客写作→段落润色→SEO关键词插入全流程

Qwen2.5-1.5B惊艳效果:技术博客写作→段落润色→SEO关键词插入全流程 1. 为什么你需要一个本地化的轻量级AI对话助手 你有没有遇到过这样的场景:想快速润色一段技术博客初稿,却担心把敏感内容发到公有云大模型;想为团队写一份产…

作者头像 李华
网站建设 2026/2/6 2:39:25

XLeRobot数字孪生系统构建指南:虚实协同控制技术解析与实践

XLeRobot数字孪生系统构建指南:虚实协同控制技术解析与实践 【免费下载链接】XLeRobot XLeRobot: Practical Household Dual-Arm Mobile Robot for ~$660 项目地址: https://gitcode.com/GitHub_Trending/xl/XLeRobot XLeRobot是一个面向家庭场景的低成本双机…

作者头像 李华
网站建设 2026/2/19 16:52:46

FSMN VAD使用避坑指南:这些参数设置少走弯路

FSMN VAD使用避坑指南:这些参数设置少走弯路 你是否也遇到过这样的情况:上传一段会议录音,FSMN VAD却把人声切得支离破碎;或者在嘈杂环境下,模型把空调声、键盘敲击声都当成了语音?明明是阿里达摩院工业级…

作者头像 李华