jar文件本质上是ZIP格式的压缩包,加上一个MANIFEST清单文件就成了Java生态里标准的分发单元。很多开发者习惯了在IDE里点一下按钮完成打包,一旦脱离IDE、需要在服务器上手工操作,就对jar命令一脸茫然。更别提SpringBoot项目打出来的可执行jar,它和普通jar的结构完全不同,运行方式也有讲究。这篇文章把jar命令的核心用法和SpringBoot运行jar包的知识串起来讲,顺带把常见的坑一一列出来。

jar命令基础用法详解
jar命令随JDK一起发布,位于JDK的bin目录下。它的参数风格和Unix的tar命令非常相似,掌握几个核心参数就能覆盖绝大多数场景。下面通过实际例子来演示。
创建一个jar包,把当前目录下所有的class文件打进去,命令是jar cf myapp.jar *.class。这里的c表示创建新文件,f指定目标文件名。如果想看打包过程的详细信息,加上v参数变成jar cvf myapp.jar *.class,控制台会列出每一个被加入的文件。
查看jar包内容用jar tf myapp.jar,t是列出目录的意思,f指定文件。这个命令在排查“包里到底有没有那个类”时特别有用。解压则是把t换成x:jar xf myapp.jar,会在当前目录还原出包内所有文件。更新已有jar包用u参数,例如往包里补充一个新的配置文件:jar uf myapp.jar application.yml。除了jar命令,你也可以直接用unzip -l myapp.jar查看内容,因为jar本质上就是zip。
一个容易被忽略的细节是清单文件。执行jar cf时如果目录下没有META-INF/MANIFEST.MF,jar工具会自动生成一个最简单的清单。而要制作可以直接用java -jar运行的包,必须在清单里声明Main-Class属性,可以用m参数指定自定义清单:
jar cvfm myapp.jar manifest.txt com/ # manifest.txt 内容示例(注意冒号后必须有一个空格,末尾需要换行): # Main-Class: com.example.Main
这里参数的书写顺序有讲究:f和m后面的第一个文件名对应f(目标jar),第二个对应m(清单文件),顺序不能颠倒,否则会把jar包名当成清单来读。
SpringBoot可执行jar的内部结构
SpringBoot通过spring-boot-maven-plugin打出来的jar,俗称fat jar,结构和普通jar差别很大。用jar tf app.jar看一下目录,你会发现三块关键内容:
META-INF/ MANIFEST.MF org/springframework/boot/loader/ # 内嵌的引导加载器 BOOT-INF/classes/ # 你自己写的代码和资源 BOOT-INF/lib/ # 所有第三方依赖的jar
关键在那个loader。打开META-INF/MANIFEST.MF,能看到Main-Class指向的是org.springframework.boot.loader.JarLauncher,而不是你自己写的启动类。真正的业务启动类写在Start-Class属性里。也就是说,java -jar执行时首先启动的是SpringBoot的加载器,由它负责搭建一个自定义的类加载器(LaunchedURLClassLoader),再去加载BOOT-INF目录下的类和依赖,最后反射调用你的main方法。
理解这个结构能解释很多现象。比如为什么fat jar不能被其他项目当作普通依赖直接引用——因为它的类都藏在BOOT-INF下,不在默认的类路径上。如果你需要提供一个给别人依赖的库包,应该另建一个模块单独打包,或者配置classifier打出一份普通jar。再比如为什么解压fat jar后直接java -cp运行会失败,也是同样的原因,必须经过JarLauncher的加载逻辑。
通过java命令运行jar的实用技巧
最基本的运行方式人人都会:java -jar app.jar。但生产环境往往需要更多控制。先说参数传递,SpringBoot项目可以通过--参数名=值的形式覆盖配置项,例如:
java -jar app.jar --server.port=8081 --spring.profiles.active=prod
注意命令行参数要放在jar包名后面,位置错了就不会生效。JVM参数则放在-jar之前,例如设置堆内存:java -Xms512m -Xmx1024m -jar app.jar。两者的作用层面不同,JVM参数控制虚拟机行为,命令行参数传给应用本身,新手常把它们混在一起写导致排错困难。
外置配置文件是另一个高频需求。SpringBoot默认会依次加载jar包同级目录下的config子目录和当前目录的application.yml,且外部文件的优先级高于包内文件。如果你的配置文件放在别的路径,可以用--spring.config.location=file:/opt/app/application.yml显式指定。调试时想知道到底哪些配置生效了,可以在启动日志中搜索“Started Application”,或者加上--debug参数查看详尽的配置来源报告。
后台长期运行推荐配合nohup或者systemd服务。nohup写法:nohup java -jar app.jar > app.log 2>&1 &,其中2>&1表示把错误输出合并到标准输出。更规范的做法是编写systemd单元文件,由系统托管进程的自动重启和开机启动,管理体验比nohup好得多。
常见问题与注意事项
第一类问题是“找不到或无法加载主类”。如果运行普通jar报这个错,多半是清单里没有Main-Class属性,或者你用java -jar去跑一个根本不可执行的依赖包。反过来,如果用java -cp去跑fat jar,同样会失败,因为SpringBoot的类不在标准位置,必须用-jar方式让JarLauncher接管。
第二类是环境相关的问题。服务器上安装了多个JDK时,要确认java -version指向的版本与项目要求一致,SpringBoot 3.x要求JDK 17以上,用JDK 8去跑会直接抛UnsupportedClassVersionError。Linux下终端输出中文乱码,可以加-Dfile.encoding=UTF-8;端口被占用时用netstat -tlnp | grep 8080或lsof -i:8080定位占用进程。
第三类是打包相关的坑。资源文件没打进包导致启动报FileNotFoundException,通常是Maven资源配置没覆盖到对应目录,检查pom里resources的配置。多模块项目中,重复依赖打进BOOT-INF/lib可能引发类冲突,可用mvn dependency:tree分析依赖关系并在pom中排除多余版本。另外,修改了外部配置文件后需要重启进程才生效,SpringBoot不会热加载外部yml,这点和有些人以为的“改完就能生效”不一样。
最后提一个安全习惯:不要把数据库密码等敏感信息明文写进要提交的配置文件,配合环境变量或配置中心使用,运行时通过--spring.datasource.password=xxx或环境变量注入,既灵活又能避免泄露风险。掌握这些内容后,从打包到部署再到排障,jar相关的操作基本都能从容应对了。
jar命令SpringBootjava运行jar包修改时间:2026-09-14 04:58:39