如何为未命名模块获取Java 9模块引用(ModuleReference)

6

更新:我在这里回答了自己的问题:

在Java 9中运行时扫描类路径/模块路径

--

[旧问题 -- 已过时:]

在Java 9中,仅通过Module对象获取ModuleReference的正确方法是什么?

考虑这两种引用java.base的方法:

Module mod = ModuleLayer.boot().findModule("java.base").orElse(null);
ModuleReference modRef = ModuleFinder.ofSystem().find("java.base").orElse(null);

mod 有一个方法 Set<String> getPackages(),但是你只能得到包的名称,不能列出每个包中的资源。

modRef 有一个方法 ModuleReader open(),而 ModuleReader 有一个方法 Stream<String> list(),可以列出模块中的资源,这正是我需要做的。

然而,对于自动(因此未命名的)模块,通过将非模块 jar 文件添加到类路径中生成,你无法从 ModuleFinder.ofSystem().find(String name)ModuleFinder.ofSystem().findAll() 获取 ModuleReference -- 你只能从 getClass().getModule() 中获取 Module 引用。

我找不到任何方法来获取自动模块的 ModuleReference。我也找不到一种从 Module 对象获取 ModuleReference 的方法,这意味着如果模块是自动和/或未命名的,则无法列出其中的资源。

肯定有一种方法可以为给定(已加载的)Module获取ModuleReference吧?


你能否更新一下问题,提供一个可复现的代码示例,并分享一下 configuration.modules() 的输出结果。 - Naman
2个回答

3
需要翻译的内容:

首先要澄清问题中的推断是有误的 -

对于通过将非模块 jar 文件添加到 classpath 中产生的自动(因此未命名)模块

自动模块是在模块路径上找到的命名模块。另一方面,未命名模块是模块系统的成员,支持从类路径加载其包未定义在任何已知(命名)模块中的类型。


加载类型本身所使用的ClassLoader可以用来获取Module(未命名)。我也尝试在回答Java 9中创建了多少个未命名模块?时解释过这一点。为了链接到精确的文档,该文档还回答了未命名模块与哪个类加载器相关联?

事实证明,每个类加载器都有其自己的唯一未命名模块, 它由新的ClassLoader::getUnnamedModule方法返回....

ClassLoader cl = getClass().getClassLoader();// returns the class loader for the class
Module yourClassLoaderUnnamedModule = cl.getUnnamedModule();

进一步的,文档添加了获取未命名模块类型加载器的模块的内容:

...该类型被认为位于该加载器的未命名模块中,即该类型的Class对象的getModule方法将返回其加载器的未命名模块。

最终等价于:

Module yourClassUnnamedModule = getClass().getModule(); // from the type itself

主要涉及访问无名模块的资源,尽管我同意目前没有可见的API来访问未命名模块的资源等。然而,由于通过此模块加载的Class对象的类型始终来自类路径,因此可以获得正在使用的类路径的所有资源,或者反过来,您可以检查当前使用的资源是从类路径还是模块路径访问的。我能想到的一种方法是使用上述两个功能进行验证:

yourClassLoaderUnnamedModule.equals(yourClassUnnamedModule)//true if resource is loaded via classpath

1

ModuleFinder.ofSystem()返回的模块查找器用于定位系统模块,即运行应用程序的JRE环境中内置的模块。

如果已知自动模块在文件系统上的位置,则可以尝试使用目录检索模块查找器,即使用ModuleFinder.of(path)。另一种方法是使用模块层的配置来解析模块引用:

Optional<ResolvedModule> resolvedModule = ModuleLayer.boot().configuration().findModule(name);
Optional<ModuleReference> moduleReference = resolvedModule.map(ResolvedModule::reference);

回答您关于如何仅从Module获取模块引用的一般问题,其中一种方法是:

Optional<ModuleReference> moduleReference
     = module.getLayer().configuration()
           .findModule(module.getName())
           .map(ResolvedModule::reference);

对于一个未命名的模块,我的猜测是您无法检索到它的ModuleReference,但至少可以尝试调用 configuration().modules()并查看它们是否包括未命名的模块。

谢谢,@manouti。关于你的第一点——在代码中手动解决模块路径问题会破坏类路径/模块路径的目的。对于你提供的两个代码示例,这需要一个模块有一个名称——而我的观点是我正在尝试为自动模块执行此操作。如果这些模块来自传统的类路径,则自动模块没有名称(getClass().getModule().getName()返回null)。只有当你将它们添加到模块路径而不是类路径时,它们才有名称(据我所知),然后从jar文件名产生一个模块名称。 - Luke Hutchison
自动模块实际上是一个“命名”的模块;只有未命名的模块没有名称。所以从你所描述的情况来看,getClass().getModule().getName()返回null,因为运行此代码的当前类属于未命名模块,而不是作为自动模块的一部分。将非模块JAR添加到类路径会使它们成为未命名模块的一部分,而自动模块则添加到模块路径中。 - M A
在这种情况下,我不确定是否有一种方法可以获取ModuleReference,但我会尝试调用boot().configuration().modules()并查看它们是否包括一个未命名的模块。 - M A
是的,你说得对,所以我只谈论未命名(classpath)模块,而不是命名自动(module path)模块。不,该调用不会返回未命名模块。关于为什么这是一个问题的更多细节,请参见我在此处发送给jigsaw-dev的电子邮件:http://mail.openjdk.java.net/pipermail/jigsaw-dev/2017-December/013439.html - Luke Hutchison

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接