Java JAR命令打包详解与坑点

一、背景

同事从第三方厂家得到了jar包,但是由于里面的一个java代码逻辑需要自己适配,在拿到源代码以后,修改了一点逻辑,最后编译生成新的class文件。我们暂且把这个文件叫Business.class。

原jar能直接运行,没有任何问题。按照替换的逻辑,我们很快能想到,就是把jar包使用windows解压工具进行解压,然后把class文件放到对应的路径,最后再zip打包为.jar文件即可。但是不出意外,还是出意外了。

通过zip打的包通过java -jar运行报错:

html 复制代码
HelloWorld.jar中没有主清单属性

仅仅只是替换了个class文件,报这个错误,让人摸不着头脑。下面就是我们的排坑之路~,花了几个小时,最后才搞定。

二、排查过程

1、针对新jar包解压,查看META-INF/MANIFEST.MF

查看了元信息文件,描述的程序入口Main-Class也是正确的。没啥毛病。 因为我们做的操作很简单,就是替换了一个class文件,整个jar都没起来。 那怀疑是通过zip打包的方式可能存在问题

2、旧jar包与新jar包同时解压,使用diff -r 递归对比2个目录看差异

diff -r $old_dir $new_dir 对比了一下,就是我们替换的那个class文件存在的jar包有差异,再继续解压里面的jar,查看文件对比,最终锁定,就只有Hello.class有差异,其它一样。

3、查看jar打包和zip直接打包的区别

通过资料查询到, 通过jar命令打包和zip直接打包,还是存在差别的。 其中存在一个差别就是,jar有一个参数0,0的意思就是只是打包不压缩。 但是可能我们使用windows可视化工具直接修改保存之后,默认就是使用了压缩算法,导致java -jar运行的时候,无法解析那个被压缩的jar文件,导致报错。

4、使用jar cvfM0 Hello.jar *的方式解决问题

使用以下jar打包命令之后,打出的新jar包能正常运行:

bash 复制代码
jar cvfM0 Hello.jar *

参数解释:

c: 创建一个jar文件

f: 文件名称

v: 显示打包log过程

M: 不创建新的META-INFO和MAIFEST.MF文件,直接使用原项目的

0: 不使用任何压缩算法,只是对打包的文件/目录进行原样打包,不压缩

5、结论验证

为了验证我们的分析结论,通过unzip -v $jar 查看每个文件打包使用的压缩算法,对比下什么情况:

左边是存在文件的jar包(基于旧jar包,重新替换了一个Hello.class文件), 右边是旧jar包(正常运行的)。

内部相同依赖的jar文件 ,我们看到左边采用了压缩算法,压缩了15%的内容。 右边Stored表示没有使用压缩算法,原样打包。

由此可见和我们的猜想是一样的。

三、jar打包命令详解

1、jar --help查看帮助信息

bash 复制代码
非法选项: -
用法: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files ...
选项:
    -c  创建新档案
    -t  列出档案目录
    -x  从档案中提取指定的 (或所有) 文件
    -u  更新现有档案
    -v  在标准输出中生成详细输出
    -f  指定档案文件名
    -m  包含指定清单文件中的清单信息
    -n  创建新档案后执行 Pack200 规范化
    -e  为捆绑到可执行 jar 文件的独立应用程序
        指定应用程序入口点
    -0  仅存储; 不使用任何 ZIP 压缩
    -P  保留文件名中的前导 '/' (绝对路径) 和 ".." (父目录) 组件
    -M  不创建条目的清单文件
    -i  为指定的 jar 文件生成索引信息
    -C  更改为指定的目录并包含以下文件
如果任何文件为目录, 则对其进行递归处理。
清单文件名, 档案文件名和入口点名称的指定顺序
与 'm', 'f' 和 'e' 标记的指定顺序相同。

示例 1: 将两个类文件归档到一个名为 classes.jar 的档案中: 
       jar cvf classes.jar Foo.class Bar.class 
示例 2: 使用现有的清单文件 'mymanifest' 并
           将 foo/ 目录中的所有文件归档到 'classes.jar' 中: 
       jar cvfm classes.jar mymanifest -C foo/ .

2、参数详解( 'm', 'f' 和 'e' 标记的指定顺序相同)

1、-c 、-f、-v

-c 表示需要创建一个jar

-f 指明jar包的文件名称

-v 在标准输出中生成详细输出

2、-t 预览jar包内容
bash 复制代码
jar -tf  hello.jar   #预览查看hello.jar文件内容
3、-u 添加/更新文件

如果需要更新jar里面的文件,可以直接进行更新。 但是本地目录和jar里面的路径要保持一致(文件覆盖场景)

4、-x解压文件或者全部解压jar

-x可以解压Hello.jar

5、-0 仅存储; 不使用任何 ZIP 压缩

-0 原样添加打包,不会对源文件进行压缩

6、-m、-M

-m 包含指定清单文件中的清单信息(创建META-INF)

-M 不创建条目的清单文件(使用原有的jar的META-INF结构)

7、-C

-C 更改为指定的目录并包含以下文件

3、常用场景指令

1、打包创建jar文件
bash 复制代码
jar cvf  Hello.jar *   #常见打包方式

jar cvfM0 Hello.jar *  #-0不压缩,打包方式
2、解压jar文件
bash 复制代码
jar xvf Hello.jar  #解压jar包
3、更新文件
bash 复制代码
jar uf Hello.jar  com/bes/Hello.class
4、预览jar内容
bash 复制代码
jar tf Hello.jar #预览Hello.jar文件内容
5、查看jar压缩详情
bash 复制代码
unzip -v Hello.jar  #查看jar包,打包详情(是否使用压缩、使用哪些压缩算法)

四、总结

建议还是按照java规范,使用jar命令进行打包处理,而不是简单的zip命令进行打包。 但是也不是一概而论,如果先通过zip没问题就可以,如果存在问题回到原点,使用jar打包即可。

特别注意-0参数是否需要,否则会造成上述的问题。

相关推荐
陈大爷(有低保)3 分钟前
UDP Socket聊天室(Java)
java·网络协议·udp
kinlon.liu17 分钟前
零信任安全架构--持续验证
java·安全·安全架构·mfa·持续验证
NiNg_1_23432 分钟前
使用Docker Compose一键部署
运维·docker·容器
萠哥啥都行37 分钟前
Linux安装Docker以及Docker入门操作
运维·docker·容器
王哲晓38 分钟前
Linux通过yum安装Docker
java·linux·docker
java66666888842 分钟前
如何在Java中实现高效的对象映射:Dozer与MapStruct的比较与优化
java·开发语言
Violet永存43 分钟前
源码分析:LinkedList
java·开发语言
小江湖199443 分钟前
元数据保护者,Caesium压缩不丢重要信息
运维·学习·软件需求·改行学it
执键行天涯44 分钟前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
gopher95111 小时前
linux驱动开发-中断子系统
linux·运维·驱动开发