如何为命名模块指定 java Manifest Add-Exports
How to specify java Manifest Add-Exports for named modules
我有一个 named 模块,它在编译时使用了一些 --add-exports
。
我如何在不在 运行 命令行中手动指定的情况下暗示该指令?
命名模块的编译
javac --add-exports java.base/sun.security.x509=mymodule --module-source-path src -d bin --module mymodule
打包为 JAR
jar -c --file=mymodule.jar --main-class=mymodule.Main -C bin/mymodule
运行
java --module-path . --module mymodule/mymodule.Main
原因:
cannot access class sun.security.x509.AlgorithmId (in module java.base) because module java.base does not export sun.security.x509 to module mymodule
- 如果为 运行 指定了
--add-exports
,则 运行 没问题
java --add-exports java.base/sun.security.x509=mymodule --module-path . --module mymodule/mymodule.Main
- 如果在 MANIFEST.MF 和 中指定了
Add-Exports
jar 运行 作为未命名的模块,它应该 运行 很好 java -jar mymodule.jar
但不是。这曾经在 JRE 11 中有效,但现在在 JRE 17 中失败(在撰写本文时是最新的)
cannot access class sun.security.x509.AlgorithmId (in module java.base) because module java.base does not export sun.security.x509 to unnamed module @0x53bd815b
http://openjdk.java.net/jeps/261 状态:
A module/package pair in the value of an Add-Exports attribute has the same meaning as the command-line option --add-exports module/package=ALL-UNNAMED
如何指定导出到 ALL-UNNAMED
而不是 mymodule
?如何让它发挥作用?
如果我的模块不是主要 class 而是一个使用更深的库,那么我必须为我有时可能直接从命令行使用的所有可能模块指定所有导出?
目前看来(JRE 17),这是不可能的。
仅在 2 种情况下执行导出:
当以命令行参数--add-exports
启动时(很明显)
当使用 -jar
选项启动程序时,它会检查 MANIFEST.MF
是否有 Add-Exports
指令。到目前为止,这是 唯一 例检查清单的情况。
这意味着以java -cp my.jar my.Main
开头将不会检查jar文件的清单。
这意味着从 java --module my.module
开始也不会检查清单。
我的看法
这很可悲,因为它破坏了 java 模块系统。如果您的依赖模块之一需要这些导出,您需要在命令行参数中设置它们,并且您需要提前知道。这通常是那些 sun.*
类 的情况,没有人应该使用但每个人都使用。 @Oracle:因此,如果我们无论如何都无法使用它,要么将其公开(因此无需添加导出),要么将其从 JRE 中完全删除...
解决方法
使用 URLClassLoader
在运行时自己动态加载依赖模块并自己进行导出(丑陋)。
String exports = jarFile.getManifest().getMainAttributes().getValue("Add-Exports");
if( exports != null )
{
for (String moduleAndPackage : exports.split(" "))
{
String[] s = moduleAndPackage.trim().split("/");
if (s.length != 2) continue;
jdk.internal.module.Modules.addExports(ModuleLayer.boot().findModule(s[0]).orElseThrow(), s[1]);
}
}
猜猜看,为了做到这一点,您需要使用受限制的 java.base/jdk.internal.module
包...如果您的程序可以使用 -jar
.
这样可以防止在命令行中手动添加所有导出(您可能不知道)。
我有一个 named 模块,它在编译时使用了一些 --add-exports
。
我如何在不在 运行 命令行中手动指定的情况下暗示该指令?
命名模块的编译
javac --add-exports java.base/sun.security.x509=mymodule --module-source-path src -d bin --module mymodule
打包为 JAR
jar -c --file=mymodule.jar --main-class=mymodule.Main -C bin/mymodule
运行
java --module-path . --module mymodule/mymodule.Main
原因:
cannot access class sun.security.x509.AlgorithmId (in module java.base) because module java.base does not export sun.security.x509 to module mymodule
- 如果为 运行 指定了
--add-exports
,则 运行 没问题java --add-exports java.base/sun.security.x509=mymodule --module-path . --module mymodule/mymodule.Main
- 如果在 MANIFEST.MF 和 中指定了
Add-Exports
jar 运行 作为未命名的模块,它应该 运行 很好java -jar mymodule.jar
但不是。这曾经在 JRE 11 中有效,但现在在 JRE 17 中失败(在撰写本文时是最新的)
cannot access class sun.security.x509.AlgorithmId (in module java.base) because module java.base does not export sun.security.x509 to unnamed module @0x53bd815b
http://openjdk.java.net/jeps/261 状态:
A module/package pair in the value of an Add-Exports attribute has the same meaning as the command-line option --add-exports module/package=ALL-UNNAMED
如何指定导出到 ALL-UNNAMED
而不是 mymodule
?如何让它发挥作用?
如果我的模块不是主要 class 而是一个使用更深的库,那么我必须为我有时可能直接从命令行使用的所有可能模块指定所有导出?
目前看来(JRE 17),这是不可能的。
仅在 2 种情况下执行导出:
当以命令行参数
--add-exports
启动时(很明显)当使用
-jar
选项启动程序时,它会检查MANIFEST.MF
是否有Add-Exports
指令。到目前为止,这是 唯一 例检查清单的情况。
这意味着以java -cp my.jar my.Main
开头将不会检查jar文件的清单。
这意味着从 java --module my.module
开始也不会检查清单。
我的看法
这很可悲,因为它破坏了 java 模块系统。如果您的依赖模块之一需要这些导出,您需要在命令行参数中设置它们,并且您需要提前知道。这通常是那些 sun.*
类 的情况,没有人应该使用但每个人都使用。 @Oracle:因此,如果我们无论如何都无法使用它,要么将其公开(因此无需添加导出),要么将其从 JRE 中完全删除...
解决方法
使用 URLClassLoader
在运行时自己动态加载依赖模块并自己进行导出(丑陋)。
String exports = jarFile.getManifest().getMainAttributes().getValue("Add-Exports");
if( exports != null )
{
for (String moduleAndPackage : exports.split(" "))
{
String[] s = moduleAndPackage.trim().split("/");
if (s.length != 2) continue;
jdk.internal.module.Modules.addExports(ModuleLayer.boot().findModule(s[0]).orElseThrow(), s[1]);
}
}
猜猜看,为了做到这一点,您需要使用受限制的 java.base/jdk.internal.module
包...如果您的程序可以使用 -jar
.
这样可以防止在命令行中手动添加所有导出(您可能不知道)。