这是我参与更文挑战的第4天,活动详情查看: 更文挑战
总文档 :文章目录
Github : github.com/black-ant
每次文章都很尽量走纯技术路线 ,为什么就没人点赞呢?????
一 . 前言
本篇文章梳理一下一个 JAR 包 , 有什么涉及的要点
1.1 JAR 简述
JAR文件 是一种基于Zip的文件格式,用于将许多文件汇总到一个 , JAR 文件本质上是一个 zip 文件,其中包含一个可选的 META-INF 目录。
JAR 命令 是基于 ZIP 和 ZLIB 压缩格式的通用归档和压缩工具。最初,jar 命令用于打包 Java applet (自 JDK 11以来不受支持)或应用程序; 然而,从 JDK 9开始,用户可以使用 jar 命令创建模块化 jar
二 . 包结构
Java 里面想运行一个项目 , 通常有 war 和 jar 2种方式 , 过去最常见的就是通过 war 包部署项目 ,
包结构作用
1 | java复制代码. |
来看一下正常包结构中各个目录的作用
// META-INF 目录
用于存储包和扩展配置数据,包括安全性、版本控制、扩展、配置应用程序、类加载器和服务 , META-INF 目录中的文件/目录由java2平台识别和解释
- MANIFEST.MF : 用于定义扩展和包相关数据的清单文件
- INDEX.LIST : 由 “-i” 命令生成 , 该文件包含应用程序或扩展中定义的包的位置信息。它是 JarIndex 实现的一部分,由类装入器用来加速其类装入过程
- x.SF : JAR 文件的签名文件. ‘ x’代表基本文件名
- x.DSA : 具有相同基文件名的签名文件关联的签名块文件。此文件存储相应签名文件的数字签名
- maven : maven 的配置消息
BOOT-INF 目录 在后文详述
附录 一 : MANIFEST.MF 文件
1 | java复制代码// 规范的版本 |
附录二 : 包签名
JavaTM 平台允许您对 JAR 文件进行数字签名 , 在对 JAR 文件进行签名时,还可以选择对签名进行时间戳 , 时间戳可用于验证用于签名 JAR 文件的证书在签名时是否有效.
为此 , 还可以配置安全策略控制 , 可以将策略配置为向 applet 和应用程序授予安全特权 ,例如,您可以授予 applet 执行通常禁止的操作的权限,例如读写本地文件或运行本地可执行程序
一个签名主要由 4个元素完成 : 私钥 , 公钥与证书 , 摘要值
签名的使用方式 : Java 平台通过使用称为公钥和私钥的特殊号码来实现签名和验证
使用流程 : 签名者使用私钥为 JAR 文件签名 , 应用的公钥与证书一起放在 JAR 文件中,以便任何想要验证签名的人都可以使用它
摘要值 : 摘要值是文件内容的散列或编码表示形式,与签名时相同。当且仅当文件本身发生变化时,文件摘要才会发生变化。
证书 : 仅使用公钥和私钥还不足以真正验证签名 , 需要某种方法来确认公钥实际上来自它声称来自的签名者 , 这就用到了证书的概念 (添加一个附加元素 , 该元素是签名者在签名 JAR 文件中包含的证书)
1 | JAVA复制代码 |
三 . 常用操作
3.1 获取 jar 包中类路径
1 | java复制代码// JAR 命令获取 |
3.2 生成tree树结构的几种方式
如果想要打印 tree 结构有以下几种方式 :
1 | java复制代码 |
3.3 JAR 反编译方法
1 | java复制代码// 方案一 : 工具查看 |
3.4 如何预防反编译问题
Java 字节码保留有关字段、方法返回值和参数的类型信息,但是它不包含本地变量的类型信息。
Java 类文件中的类型信息使得字节码的反编译任务比机器代码的反编译更加容易。
因此,反编译 Java 字节码需要分析大多数局部变量类型,平坦堆栈指令和结构化循环和条件。
方案一 : 使用 YGuard 混淆类文件 (类似的还有 ProGuard 等 )
可以将已编译的代码转换为人类难以理解的代码 , 同时有助于减少应用程序的启动时间.
最重要的是 , 这是一个完全开源的程序. YGuard 的混淆方式 :
- 通过使用不可表达的字符替换包、类、方法和字段名,从逆向工程中删除类文件
- 收缩引擎分析所有输入 Jar 文件的字节码,以确定从一组给定的代码入口点无法到达哪些代码实体。然后 yGuard 将删除这些过时的代码片段(整个类或单个方法和字段)。
YGuard 模糊的是 class 文件 , YGuard 是一个免费的开源 Java 字节码混淆器和 shrinker , 通过模糊处理,参考地址 :
1 | java复制代码<dependency> |
方案二 : 如果想要隐藏某个字符串 , 可以考虑对属性加密
方案三 :很多反编译器没有适应新特性和某些工具框架 . 例如 : lambdas
方案四 :定制一个 ClassLoader 用于专门的解密
方案五 : 使用 protector4j 对 jar 文件加密 , 将 JAR 文件转换为私有的 JARX 格式,保护类文件和应用程序结构
@ protector4j.com/(PS : 个人没用过 , 不评价 , 类似的还有 JarProtector , 不过 protector4j 比较新 )
3.5 Maven 打包 JAR
1 | xml复制代码<!-- Maven pom.xml 文件构建 jar--> |
3.6 把文件打包到 JAR 中
可以使用 jar -u 命令更新 jar 中的文件 , 常常用于系统的定制等操作 , 通常的格式如下 :
1 | java复制代码 |
四 . 其他操作
4.1 手动创建一个 JAR 文件
在这个案例里面 , 我会试着手动编译 .java 并且打包成一个 jar 文件并且执行
1 | java复制代码// Step 1 : 准备一个 .java 方法 |
4.2 Maven 添加 MANIFEST.MF 文件
这种方式是通过 Maven 编辑 MANIFEST.MF 文件
1 | xml复制代码<plugin> |
4.3 JAR 添加 MANIFEST.MF 文件
通过 JAR 命令 手动添加 MANIFEST.MF , 其中最重要的一点 : 使用换行符结束清单文件 , 否者默认会忽略
1 | java复制代码// Step 1 : 准备一个任意的文本文件 , 添加属性(记得回车) |
4.4 JAR 中定义 JDK 多版本
当期望 JAR 包能在多种版本的 JDK 运行时 , 可以使用如下方式构建 :
1 | xml复制代码├── pom.xml |
4.5 Fat JAR 打包
什么是 Fat jat ?
Fat jar 同样是一个 jar , 与普通 jar 的区别是 : 它包含来自所有库的类,您的项目依赖于这些库,当然还有当前项目的类.
以 Spring 的打包方式为例 : Spring 将应用程序代码打包到BOOT-INF.classes,将依赖包打包到BOOT-INF.lib目录
Spring 这种应该就是 Fat JAR “ 将 dependencies 的 jar 复制到主 jar,然后使用特殊的类加载器加载
1 | xml复制代码 |
五 . JAR 命令及常见操作
5.1 JAR 命令模板
jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 ...
其中 {ctxu} 是 jar 命令的子命令,每次 jar 命令只能包含 ctxu 中的一个,它们分别表示
-c : 创建新的 JAR 文件包
-t : 列出 JAR 文件包的内容列表
-x : 展开 JAR 文件包的指定文件或者所有文件
-u : 更新已存在的 JAR 文件包 (添加文件到 JAR 文件包中)
[vfm0M] : 中的选项可以任选,也可以不选,它们是 jar 命令的选项参数
-v : 生成详细报告并打印到标准输出
-f : 指定 JAR 文件名,通常这个参数是必须的
-m : 指定需要包含的 MANIFEST 清单文件
-0 : 只存储,不压缩,这样产生的 JAR 文件包会比不用该参数产生的体积大,但速度更快
-M : 不产生所有项的清单(MANIFEST〕文件,此参数会忽略 -m 参数
[jar-文件] : 即需要生成、查看、更新或者解开的 JAR 文件包,它是 -f 参数的附属参数
[manifest-文件] : 即 MANIFEST 清单文件,它是 -m 参数的附属参数
[-C 目录] : 表示转到指定目录下去执行这个 jar 命令的操作。它相当于先使用 cd 命令转该目录下再执行不带 -C 参数的 jar 命令,它只能在创建和更新 JAR 文件包的时候可用。
文件名 … : 指定一个文件/目录列表,这些文件/目录就是要添加到 JAR 文件包中的文件/目录。如果指定了目录,那么 jar 命令打包的时候会自动把该目录中的所有文件和子目录打入包中。
5.2 JAR 常用命令
@ jar-The Java Archive Tool (oracle.com)
一 : JAR 命令设置 Class 主类
1 | java复制代码jar cfe example.jar com.gang.DefaultMain com/gang/*.class |
二 : 运行 jar
1 | JAVA复制代码java -jar test.jar |
三 : jar 内容管理
1 | java复制代码// 提取 JAR 内容 |
四 : 清单管理
1 | java复制代码// 命令行修改清单文件 |
其他检索
1 | java复制代码// 指定主类 : |
六 . 其他
6.1 WAR 与 JAR 的区别
JAR 包
简单地说,JAR 或 Java Archive 是一种包文件格式 , JAR 文件具有 .Jar 扩展名 , 可能包含库、资源和元数据文件 , 实际上,它是一个压缩文件,包含. 类文件的压缩版本以及编译的 Java 库和应用程序的资源
WAR 包
WAR 是 Web Application Archive 或 Web Application Resource 的缩写 , 这些归档文件具有 .War 扩展,用于打包 web 应用程序,这些应用程序可以部署在任何 Servlet/JSP 容器上。
1 | java复制代码// WAR 包格式 |
6.2 java.util.jar api 使用
6.3 对比 BOOT-INF
前面看到还有一个 BOOT-INF , 这个文件夹的目的又是什么 ?
BOOT-INF 结构的特性 :
- Spring Boot 应用程序加载表单 BOOT-INF 文件夹
- 应用程序类应该放在嵌套的 BOOT-INF/classes 目录中
- 依赖项应该放在一个嵌套的 BOOT-INF/lib 目录中
BOOT-INF 解决的问题 :
Java 没有提供任何加载嵌套 jar 文件的标准方法(即加载本身包含在 jar 中的 jar 文件)。当需要分发一个可以从命令行运行而不需要解压缩的自包含应用程序时 , 会出现问题
PS : 其实这里就是前文说的 FAT JAR
为此 , Spring 采用了如下的目录结构 , 将 应用程序类放在一个嵌套的 BOOT-INF/classes 目录中
1 | JAVA复制代码example.jar |
BOOT-INF 的索引
前面说了 , BOOT-INF 说到底是为了聚合包和应用程序 , 他有多种索引文件 :
- Classpath Index (classpath.idx) : 提供了将 jars 添加到类路径的顺序
- Layer Index (layers.idx) : 提供了一个 Layer 和 JAR 中应该包含的部分的列表
1 | JAVA复制代码// classpath.idx |
BOOT-INF 的加载
BOOT-INF 基于 org.springframework.boot.loader.jar 进行加载 , 该类允许从标准 jar 文件或嵌套的子 jar 数据中加载 jar 内容
首次加载时,每个 JarEntry 的位置映射到外部 jar 的物理文件偏移量
1 | java复制代码myapp.jar |
有了这些信息,我们就可以通过寻找外部 jar 的适当部分来加载特定的嵌套条目 , 这就是 BOOT-INF 解决外部以来的主要方式
同时 , Spring 提供了 多个启动器装载资源 :
- Springframework.boot.loader.Launcher
- 该类是一个特殊的引导类,用作可执行 jar 的主入口点。它是 jar 文件中的实际 Main-Class,用于设置适当的 URLClassLoader 并最终调用 main ()方法
- JarLauncher : 子启动器 , 在 BOOT-INF/lib/中查找
- WarLauncher : 子启动器 , 在 WEB-INF/lib/和 WEB-INF/lib-proved/中查找
- PropertiesLauncher : 子启动器 , 默认在应用程序归档中查找 BOOT-INF/lib/ (允许添加额外路径)
1 | java复制代码// 配置方式 : |
总结
不知道怎么写总结了 , 本来就是一篇总结文档 , 所以没什么需要总结的..
只是想吐槽一下 , 一直以来都在走技术路线 , 但是深深的感觉长篇幅的技术文档 , 受众面很小. 虽然写文档本身还是为了服务自己 , 但是没人看确实影响心态 , 一度怀疑是不是总结的不好 , 以后自己用来回顾的时候会不会也看不懂了~~
参考
官方参考文档
其他参考文档
- www.baeldung.com/jar-file-ge…
- www.baeldung.com/java-view-j…
- www.baeldung.com/java-view-j…
- www.baeldung.com/install-loc…
- www.baeldung.com/executable-…
- www.baeldung.com/deployable-…
- www.baeldung.com/java-jar-ex…
- www.baeldung.com/java-create…
- www.baeldung.com/java-jar-wa…
- www.baeldung.com/java-jar-ma…
- www.baeldung.com/maven-multi…
本文转载自: 掘金