Java JAR命令打包详解与坑点

ops/2024/10/21 5:35:14/

一、背景

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

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

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

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包能正常运行:

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查看帮助信息

非法选项: -
用法: 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包内容
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文件
jar cvf  Hello.jar *   #常见打包方式jar cvfM0 Hello.jar *  #-0不压缩,打包方式
2、解压jar文件
jar xvf Hello.jar  #解压jar
3、更新文件
jar uf Hello.jar  com/bes/Hello.class
4、预览jar内容
jar tf Hello.jar #预览Hello.jar文件内容
5、查看jar压缩详情
unzip -v Hello.jar  #查看jar包,打包详情(是否使用压缩、使用哪些压缩算法)

四、总结

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

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


http://www.ppmy.cn/ops/103922.html

相关文章

uniapp分包

分包是指将一个大的应用程序拆分成多个小的、可以独立加载的部分。这样做的好处是可以显著减少初次加载时的数据量,从而加快启动速度,节省存储空间。 分包的实现方法 1. 配置分包 在manifest.json文件中定义分包配置。 {"pages": ["p…

Linux 数据结构 顺序表 链表

数据结构: 1.衡量一个程序是否优秀: 1.时间复杂度: 数据量增长与程序运行时间的比例关系以函数描述称为时间渐进复杂度函数,简称时间复杂度 O(c) > O(logn) > O(n) > O(nlogn) > O(n^2) > O(n^3) > O…

【电力系统】使用电力系统稳定器 (PSS) 和静态 VAR 补偿器 (SVC) 提高瞬态稳定性

摘要 电力系统在面对故障和扰动时,其瞬态稳定性是确保系统安全运行的关键因素。本文探讨了通过使用电力系统稳定器(PSS)和静态VAR补偿器(SVC)来提高电力系统瞬态稳定性的策略。通过仿真分析,证明了PSS和SV…

EmguCV学习笔记 VB.Net 8.4 pyrMeanShiftFiltering

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

css加载一张图片 设置整个页面背景

前言 在css中,可以使用background-image或background属性来加载图片。这两个属性都可以配合url()函数来为元素设置背景图像。background-image属性为元素设置背景图像,而background是一个简化属性,可以在一个声明中设置所有背景样式&#xf…

使用终端工具Tabby(跳板机)连接ssh

目录 1.tabby下载2.下载安装3.安装成功3.1在应用中配置语言为中文3.2外观可以更改字体大小颜色等3.3配色方案改变外观颜色3.4快捷键,可以根据自己的习惯自定义修改3.5配置和连接(最重要的,安装完成要连接ssh可以直接到这一步) 1.t…

动态IP池在数据抓取中的应用与优势

随着互联网技术的快速发展,数据抓取(Web Scraping)已经成为获取互联网信息的重要手段。然而,在进行大规模数据抓取时,往往会遇到反爬虫机制、IP封禁等问题。动态IP池作为一种解决方案,可以有效地绕过这些障…

读取xml的内容并显示在textEdit中,导出xml文件

使用QXmlStreamReader方法读取xml文件 #include "mainwindow.h" #include "ui_mainwindow.h" #include <QStandardItemModel> #include <QtXml> std::vector<std::map<QString, QString>> dataVector;MainWindow::MainWindow(QWi…