关于java:一文读懂jar包的小秘密

3次阅读

共计 4554 个字符,预计需要花费 12 分钟才能阅读完成。

简介

java 程序员每天不是在创立 jar 包就是在创立 jar 包的路上,并且各种依赖援用都是以 jar 包的模式展现的。然而随着古代 IDE 的呈现,我想很多程序员曾经基本上很少间接和 jar 包打交道了。

换句话说,他们曾经不意识 jar 包了。

那么 jar 包到底是什么呢?它有哪些小机密呢?一起来看一下吧。

jar 包到底是什么

jar 包其实是一种 zip 格局的文件,所以说你实际上是能够应用 zip 相干的命令来对 jar 包进行创立或者解压缩操作。

不同的是 jar 包中多了一个 META-INF 文件夹。通过这个文件夹,jar 包能够执行更多的操作。

JDK 也自带了一个 jar 命令,通过 jar 命令咱们能够实现创立,更新 jar 包的操作,下图是 JDK8 中 jar 命令的阐明:

因为 JDK9 之后引入了模块化的概念,所以 JDK9 之后 jar 命令有了比拟大的变动:

咱们看一下 JDK14 中的 jar 命令的用法:

这里次要不是讲 jar 命令,所以咱们不具体开展。

META-INF 目录

jar 包和 zip 包最大的区别就在于 jar 包中蕴含了 META-INF 目录(不是必须的),咱们看一个比拟罕用的 lombok.jar 包的构造是怎么样的:

这个版本比拟新,所以它应用的是最新的 JPMS 的写法,大家能够看到在 jar 包的根目录上面有一个 module-info.class 文件,示意这个 jar 包应用的是模块化。

而后再看一下 META-INF 目录,外面有一个 MANIFEST.MF 文件:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.1
Created-By: 14.3-b01-101 (Apple Inc.)
Premain-Class: lombok.launch.Agent
Agent-Class: lombok.launch.Agent
Can-Redefine-Classes: true
Main-Class: lombok.launch.Main
Lombok-Version: 1.18.10

MANIFEST.MF 次要用来定义 package 相干的数据,这里咱们能够看到 lombok 的 MANIFEST.MF 文件定义了 manifest 的版本号,创立工夫,版本号和几个类型的 class。

services 文件夹外面寄存的能够对外提供的服务。

这里列出的文件并不全,实际上还有上面几种文件:

  • INDEX.LIST

能够应用 - i 在生成 jar 包的时候主动创立,是 class 的 index 文件,次要用来减速 class 加载。

  • x.SF

JAR 包的签名文件。

  • x.DSA

与具备雷同根本文件名的签名文件关联的签名块文件。该文件存储相应签名文件的数字签名。

  • versions/

次要为应用多版本的个性筹备的,外面存储的是不同版本的 class 和资源。

比方上面命令创立了多个版本发行的 jar 包,并且将一些文件放在 META-INF/versions/9 目录中。

 jar --create --file mr.jar -C foo classes --release 9 -C foo9 classes

module-info.class

如果咱们应用的是 JDK9 之后的 JPMS 模块化,那么就会生成这么一个 module-info.class。这个文件次要是形容模块和内部模块间接的关系。

看一下 lombok 的例子:

module lombok {
    requires java.compiler;
    requires java.instrument;
    requires jdk.unsupported;
    requires static org.mapstruct.processor;

    exports lombok;
    exports lombok.experimental;
    exports lombok.extern.apachecommons;
    exports lombok.extern.java;
    exports lombok.extern.jbosslog;
    exports lombok.extern.log4j;
    exports lombok.extern.slf4j;
    exports lombok.extern.flogger;

    provides javax.annotation.processing.Processor with lombok.launch.AnnotationProcessorHider$AnnotationProcessor;
    provides org.mapstruct.ap.spi.AstModifyingAnnotationProcessor with lombok.launch.AnnotationProcessorHider$AstModificationNotifier;
}

这外面咱们定义了依赖的类和 service providers,同时也定义了对外提供的类。

在 JDK9 之后,存在两种 path,一种是之前的 class path,一种是 module path。当 modular JAR 被部署在 module path 中的时候,它就是一个 modular JAR。当他被部署在 class path 中的时候,就是一个 non-modular JAR。

同样的,如果是一个 non-modular JAR 被定义在 module path 中,那么这个 non-modular JAR 就主动被转换成了一个 automatic module。

如果 jar 包在 MANIFEST.MF 中定义了 Automatic-Module-Name,那么 module 名字就是这个值,否则会从 JAR 的名字来定义这个 module。

automatic module 次要是为了向下兼容而产生的。

对于 JPMS 的更多信息能够参考我之前写的文章:JDK9 的新个性:JPMS 模块化.

versions

versions 次要和 multi-release JAR 一起应用的:

Multi-Release: true

所谓 multi-release JAR 就是说一个 jar 包能够反对不同版本的 JDK。咱们能够依据须要指定不同版本的 JDK 所依赖的 class 文件或者属性文件。这个个性在咱们进行 JDK 降级的时候还是很有帮忙的。

一般来说,目录构造是这样的:META-INF/versions/N

其中 N 示意的是 JDK 的次要发行版本,比方 9,10,11 等。

类加载器会先去 META-INF/versions/ N 目录中加载所须要的 class,而后会去其余的低版本的 META-INF/versions/ N 目录中加载所须要的 class,最初才会在 META-INF/ 的根目录加载其余的 class 文件。

MANIFEST.MF 详解

MANIFEST.MF 中寄存的是 key:value 格局的配置信息,这些配置信息又能够分成两局部,第一局部是 main-section 信息,第二局部是 individual-section。

咱们举个简略的例子:

Manifest-Version: 1.0
Created-By: 1.8 (Oracle Inc.)
Sealed: true
Name: foo/bar/
Sealed: false

其中

Manifest-Version: 1.0
Created-By: 1.8 (Oracle Inc.)
Sealed: true

就是 main-section 信息,咱们用一张图来看一下 main-section 的信息有哪些:

在 main-section 信息下发能够接一个 Name: Value,示意开启独立的针对于具体 entry 的属性(Per-Entry Attributes)配置:

Name: foo/bar/
Sealed: false

比方下面的属性是专门针对于包 foo/bar/ 的,并且设置其 Sealed 属性为 false。

Per-Entry Attributes 除了 package versioning 和 sealing 信息外,还能够定义 Content-Type,Java-Bean,x-Digest- y 和 Magic 属性。

JAR 包签名

JAR 包能够通过应用 jarsigner 来对其进行签名。和签名相干的文件是:

  • META-INF/MANIFEST.MF
  • META-INF/*.SF
  • META-INF/*.DSA
  • META-INF/*.RSA
  • META-INF/SIG-*

签名过后的 jar 跟原来的 jar 其实并没有什么不同,只不过在 META-INF/ 文件夹中多出了两个文件,一个是签名文件,一个是签名 block 文件。

签名文件

签名文件是以.SF 结尾的,这个文件和 MANIFEST.MF 很相似,能够指定 Signature-Version 和 Created-By。

除此之外,还能够指定和平安相干的属性:

  • x-Digest-Manifest-Main-Attributes:其中 x 是 java.security.MessageDigest 中指定的算法,示意的次要属性的摘要。
  • x-Digest-Manifest:示意的是整个 manifest 的摘要。

这两个属性次要用来做验证签名用的。

举个例子:

如果咱们的 manifest 是上面这样的:

    Manifest-Version: 1.0
    Created-By: 1.8.0 (Oracle Inc.)

    Name: common/class1.class
    SHA-256-Digest: (base64 representation of SHA-256 digest)

    Name: common/class2.class
    SHA1-Digest: (base64 representation of SHA1 digest)
    SHA-256-Digest: (base64 representation of SHA-256 digest)

那么相应的签名文件应该是这样的:

    Signature-Version: 1.0
    SHA-256-Digest-Manifest: (base64 representation of SHA-256 digest)
    SHA-256-Digest-Manifest-Main-Attributes: (base64 representation of SHA-256 digest)

    Name: common/class1.class
    SHA-256-Digest: (base64 representation of SHA-256 digest)

    Name: common/class2.class
    SHA-256-Digest: (base64 representation of SHA-256 digest)

签名文件的摘要

如果再对.SF 文件进行摘要,那么就会失去签名文件的摘要文件:

  • .RSA (PKCS7 signature, SHA-256 + RSA)
  • .DSA (PKCS7 signature, DSA)

Sealed

下面咱们讲到了一个 Sealed 属性:

Name: javax/servlet/internal/
Sealed: true

这个属性的意思是,javax/servlet/internal/ 包中的所有类必须从这个 jar 包中加载。

这个属性次要是从 jar 包的安全性来思考的。

本文已收录于 http://www.flydean.com/java-jar-in-detail/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

正文完
 0