使用 jar 工具创建多版本 Jar

jar 命令可用于创建一个多版本 Jar,其中包含为 Java 8 和 Java 9 编译的同一类的两个版本,尽管有警告告诉这些类是相同的:

C:\Users\manouti>jar --create --file MR.jar -C sampleproject-base demo --release 9 -C sampleproject-9 demo
Warning: entry META-INF/versions/9/demo/SampleClass.class contains a class that
is identical to an entry already in the jar

--release 9 选项告诉 jar 在 MRJAR 的版本化条目内,即在 root/META-INF/versions/9 下包含后面的所有内容(sampleproject-9 目录中的 demo 包)。结果如下:

jar root
  - demo
     - SampleClass.class
  - META-INF
     - versions
        - 9
           - demo
              - SampleClass.class

现在让我们创建一个名为 Main 的类,它打印 SampleClass 的 URL,并为 Java 9 版本添加它:

package demo;

import java.net.URL;

public class Main {

    public static void main(String[] args) throws Exception {
        URL url = Main.class.getClassLoader().getResource("demo/SampleClass.class");
        System.out.println(url);
    }
}

如果我们编译这个类并重新运行 jar 命令,我们会收到一个错误:

C:\Users\manouti>jar --create --file MR.jar -C sampleproject-base demo --release 9 -C sampleproject-9 demoentry: META-INF/versions/9/demo/Main.class, contains a new public class not found in base entries
Warning: entry META-INF/versions/9/demo/Main.java, multiple resources with same name
Warning: entry META-INF/versions/9/demo/SampleClass.class contains a class that
is identical to an entry already in the jar
invalid multi-release jar file MR.jar deleted

原因是 jar 工具阻止将公共类添加到版本化条目(如果它们也未添加到基本条目中)。这样做是为了使 MRJAR 为不同的 Java 版本公开相同的公共 API。请注意,在运行时,不需要此规则。它可能只适用于像 jar 这样的工具。在这种特殊情况下,Main 的目的是运行示例代码,因此我们只需在基本条目中添加一个副本。如果该类是我们只需要 Java 9 的新实现的一部分,那么它可以是非公开的。

要将 Main 添加到根条目,我们首先需要编译它以定位 Java 9 之前的版本。这可以使用 javac 的新 --release 选项来完成:

C:\Users\manouti\sampleproject-base\demo>javac --release 8 Main.java
C:\Users\manouti\sampleproject-base\demo>cd ../..
C:\Users\manouti>jar --create --file MR.jar -C sampleproject-base demo --release 9 -C sampleproject-9 demo

运行 Main 类显示 SampleClass 从版本化目录加载:

C:\Users\manouti>java --class-path MR.jar demo.Main
jar:file:/C:/Users/manouti/MR.jar!/META-INF/versions/9/demo/SampleClass.class