一个 JAR 里可以放多套针对不同 Java 版本的实现。这篇真实打包、在三个 JDK 上运行,验证 Multi-Release 属性、系统属性开关、jar 工具的检查,以及 Maven 的构建配置。
jar tf mr.jar:
META-INF/
META-INF/MANIFEST.MF
lib/
lib/Impl.class
META-INF/versions/17/
META-INF/versions/17/lib/
META-INF/versions/17/lib/Impl.class
META-INF/versions/21/
META-INF/versions/21/lib/
META-INF/versions/21/lib/Impl.class
META-INF/MANIFEST.MF 内容: 'Manifest-Version: 1.0\r\nCreated-By: 21.0.12.1 (Homebrew)\r\nMulti-Release: true'
JDK 17 加载:versions/17 的实现(用了 record 之前的 Java 17 API:xx);id=17;Impl 来自 mr.jar
JDK 21 加载:versions/21 的实现(1);id=21;Impl 来自 mr.jar
JDK 27 加载:versions/21 的实现(1);id=21;Impl 来自 mr.jar
应用 Main 是用 --release 8 编译的,与 JDK 无关;同一个 mr.jar,JDK 17 取 versions/17,JDK 21 与 27 取 versions/21(没有 versions/27 时取最高的 ≤ 当前版本) 去掉 Multi-Release 之后的 MANIFEST.MF: 'Manifest-Version: 1.0\r\nCreated-By: 21.0.12.1 (Homebrew)' ;jar 里仍有 META-INF/versions/21/lib/Impl.class: True
JDK 21 加载:base(Java 8 版本);id=8;Impl 来自 mr_nomanifest.jar
(JDK 21)去掉属性后,jar 里的 versions 目录被当作普通目录,只用 base 类
JDK 21 加 -Djdk.util.jar.enableMultiRelease=false(jar 本身是正确的多版本包):
JDK 21 加载:base(Java 8 版本);id=8;Impl 来自 mr.jar
JDK 21 加 -Djdk.util.jar.version=17:
JDK 21 加载:versions/17 的实现(用了 record 之前的 Java 17 API:xx);id=17;Impl 来自 mr.jar
JDK 21 加 -Djdk.util.jar.version=9(没有 versions/9,取 base):
JDK 21 加载:base(Java 8 版本);id=8;Impl 来自 mr.jar 版本化的 Impl 比 base 多一个 public 方法 extra() → jar rc = 1 ; ['entry: META-INF/versions/17/lib/Impl.class, contains a class with different api from earlier version', 'invalid multi-release jar file bad1.jar deleted']
版本化目录里有 base 里不存在的 public 类 Newcls → jar rc = 1 ; ['entry: META-INF/versions/17/lib/Newcls.class, contains a new public class not found in base entries', 'invalid multi-release jar file bad2.jar deleted']
--release 8(版本化目录要求 ≥ 9)→ jar rc = 1 ; ['release 8 not valid, must be >= 9', "Try `jar --help' for more information."]
JarFile(runtimeVersion = 8).getEntry("lib/Impl.class").getRealName() = lib/Impl.class,isMultiRelease = true
JarFile(runtimeVersion = 17).getEntry("lib/Impl.class").getRealName() = META-INF/versions/17/lib/Impl.class,isMultiRelease = true
JarFile(runtimeVersion = 21).getEntry("lib/Impl.class").getRealName() = META-INF/versions/21/lib/Impl.class,isMultiRelease = true
URLClassLoader(jar) 在 JDK 17 上加载 lib.Impl → id = 17
同样用 jar: URL 加载 → id = 17
JarFile(runtimeVersion = 8).getEntry("lib/Impl.class").getRealName() = lib/Impl.class,isMultiRelease = true
JarFile(runtimeVersion = 17).getEntry("lib/Impl.class").getRealName() = META-INF/versions/17/lib/Impl.class,isMultiRelease = true
JarFile(runtimeVersion = 21).getEntry("lib/Impl.class").getRealName() = META-INF/versions/21/lib/Impl.class,isMultiRelease = true
URLClassLoader(jar) 在 JDK 21 上加载 lib.Impl → id = 21
同样用 jar: URL 加载 → id = 21mvn package rc = 0 Maven 产出的 jar 里的条目: ['META-INF/versions/11/lib/Impl.class', 'lib/Impl.class'] ;Multi-Release 行: ['Multi-Release: true'] JDK 17 加载:versions/11 的实现(xx);id=11;Impl 来自 mr-1.jar JDK 21 加载:versions/11 的实现(xx);id=11;Impl 来自 mr-1.jar jar --describe-module: No module descriptor found. Derived automatic module. mr@1 automatic requires java.base mandated contains lib