非IDE及Gradle/Maven环境下,如何通过Manifest打包含依赖的可执行Jar?
首先直接给你结论:仅靠Manifest文件本身无法实现将依赖Jar打包进主Jar并实现单Jar分发——Manifest的Class-Path属性只能指定外部依赖的相对路径,无法嵌入依赖内容。但我们可以通过手动嵌入依赖的class文件来实现单Jar分发,全程只用JDK自带的jar命令,完全不需要IDE或Gradle/Maven。
下面是具体的步骤:
1. 准备文件结构
假设你已经编译好自己的主类(比如com.example.MyMain,编译后的class文件在bin/com/example/MyMain.class),并且有第三方依赖Jar(比如guava.jar)。
先创建一个临时目录来整理所有需要打包的文件:
mkdir build-temp cd build-temp
2. 嵌入依赖的class文件
把第三方依赖Jar里的所有class文件解压到临时目录,再把你的主class文件复制进来:
# 解压依赖Jar到当前目录 unzip ../guava.jar # 复制你的主class文件到对应目录(保持包结构) cp -r ../bin/com .
这一步完成后,build-temp里会包含所有依赖的class文件和你的主class文件,包结构保持正确。
3. 创建Manifest文件
在build-temp目录下创建一个manifest.mf文件,内容如下:
Main-Class: com.example.MyMain
⚠️ 注意:Manifest文件的最后一行必须是空行,否则jar命令会解析失败。
4. 打包生成可执行Jar
用JDK的jar命令打包,指定Manifest文件和所有要包含的内容:
jar cvfm my-executable.jar manifest.mf *
c: 创建新Jarv: 显示打包过程的详细信息f: 指定Jar文件名m: 指定Manifest文件
5. 测试运行
现在你可以直接运行这个Jar包,而且它可以分发到任意目录使用:
java -jar my-executable.jar
为什么仅靠Manifest不行?
Manifest里的Class-Path属性是用来指定外部Jar的相对路径,比如:
Main-Class: com.example.MyMain Class-Path: guava.jar
这种方式要求guava.jar必须和你的主Jar在同一目录下(或者符合指定的相对路径),一旦把主Jar移到其他目录,依赖就找不到了,所以无法实现单Jar分发。
可选(复杂)方案:自定义类加载器
如果你不想解压依赖Jar的class文件,也可以把依赖Jar作为资源打包进主Jar,然后编写一个自定义类加载器来读取这些内嵌的Jar资源。但这种方法需要额外编写代码,手动实现类加载逻辑,对于简单场景来说没必要,不如直接解压嵌入class文件来得直接。
内容的提问来源于stack exchange,提问作者user8618419

