还在因 JDK 兼容问题发不同 JAR 包做兼容?MRJAR 了解一下?
Java 9 版本中增强了 Jar 包多版本字节码文件格式支持,也就是说在同一个 Jar 包中我们可以包含多个 Java 版本的 class 文件,这样就能做到 Jar 包升级到新的 Java 版本(新特性 API 使用)时不用强迫使用方为了使用新 Jar 包而升级自己的业务模块 Java 版本,也不用针对不同最低支持 Java 版本提供不同的 Jar,真正的做到了一个 Jar 包兼容所有的目的。这样的 Jar 称为 MRJAR。
MRJAR 中的代码包含在不同版本 JDK 下编译的 class 文件。譬如使用 JDK9 编译的类可以调用 JDK9 提供的 API,而使用 JDK8 编译的类可以调用 JDK8 提供的 API,只要保证他们在 MRJAR 中的包名、类名、类对外调用都一致即可。
一般典型情况下的 JAR 包内部包含 class 文件和一个属性META-INF/MANIFEST.MF
文件,如下:
jar-root
A.class
B.class
C.class
META-INF
MANIFEST.MF
MRJAR 扩展自 JAR 的目录结构,扩展了META-INF
目录以便存储特定 JDK 版本的 class 文件,META-INF
目录包含一个版本子目录,其中可能包含许多子目录,每个目录的命名需要与 JDK 主要版本相同。譬如,对特定于 JDK9 的 class 可以放在META-INF/versions/9
目录下,对特定于 JDK10 的 class 可以放在META-INF/versions/10
目录下等。所以一般典型情况下的 MRJAR 包内部大致如下:
jar-root
A.class
B.class
C.class
META-INF
MANIFEST.MF
versions
9
A.class
D.class
10
A.class
B.class
上面的例子中在不同 JDK 环境下运行表现如下:
如果这个 MRJAR 在不支持 MRJAR 的 JDK 环境(譬如 JDK8)下使用,则会被自动兼容当做普通 JAR 使用,即
META-INF/versions/
目录下都被忽略,直接使用了 root 下的 class,所以无法访问 D。如果这个 MRJAR 在 JDK9 中使用,则只有 A、B、C、D 这几个 class 可以使用,且这里的 A、D 用的是
META-INF/versions/9/
下面的 class,其他用的 root 目录下的 class。对于 JDK10 来说,原理类同上面 JDK9,这里不多解释。只是说当我们在 JDK10 环境使用 C 类时搜索的顺序是先搜索
META-INF/versions/10/
下是否存在 C,如果不存在则搜索`META-INF/versions
/9/`下是否存在 C,如果不存在则搜索根 root 目录下是否存在,这个查找顺序一定要明白。
对 JDK11 来说,因为不存在
META-INF/versions/11/
,所以依次在低于自己的版本中搜索,在这里也就等同于在 JDK10 下被命中的类。
JDK9 对生成 JAR 包的各种命令和工具都升级为支持 MRJAR,所以制作 MRJAR 最好直接使用 JDK9 开始的环境,其 jar 命令新增了一个参数为--release
,语法如下:
//N 代表一个 JDK 主版本,如 JDK9 中的 9,且 N 值必须大于等于 9;
//所有在--release N 之后的文件都会被添加到 MRJAR 的 META-INF/versions/N 目录下;
jar <options> --release N <other-options>
假设现在我们准备好了一套 JDK8 的类和一套 JDK12 的类和编译成 class 的产物,如下:
//JDK8 的源文件,编译产物目录假设为 jdk8/build/classes/
package cn.yan.mrjar;
public class JarUtil {
public String func() {
//JDK8 API 实现功能
}
}
//JDK12 的源文件,编译产物目录假设为 jdk12/build/classes/
package cn.yan.mrjar;
public class JarUtil {
public String func() {
//JDK12 API 实现功能
}
}
评论