未命名模块与 ServiceLoader::load 的命名模块交互

And*_*sha 5 java java-9 java-module module-info

我有一个这样的项目:

\---main
    \---src
        \---com.foo
            \---UnnamedStart.java
\---api
    \---src
        \---com.foo.api
            \---ApiInterface.java
        \---module-info.java
\---impl
    \---src
        \---com.foo.impl
            \---ApiInterfaceImpl.java
        \---module-info.java
Run Code Online (Sandbox Code Playgroud)

的实现UnnamedStart.java是:

public static void main(String[] args) {
    ServiceLoader<ApiInterface> services = ServiceLoader.load(ApiInterface.class);
    ...
}
Run Code Online (Sandbox Code Playgroud)

请注意,main是未命名的模块。

api/src/module-info.java 是:

module com.foo.api {
     exports com.foo.api;
}
Run Code Online (Sandbox Code Playgroud)

并且impl/src/module-info.java是:

更新 1.1 - 下面的代码更新见评论,添加requires

更新 1.2 - 下面的代码已更新,在创建问题时provides A with B更改为provides B with A错误,原本没问题

module com.foo.impl {
     requires com.foo.api; //added (update 1.1)
     provides com.foo.impl.ApiInterface
         with com.foo.api.ApiInterfaceImpl; //vice versa (update 1.2)
}
Run Code Online (Sandbox Code Playgroud)

当我运行我的代码时,UnnamedStart.java我最终在services.

我还尝试在以下位置创建静态方法com.foo.api.ApiInterface

static List<ApiInterface> getInstances() {
    ServiceLoader<ApiInterface> services = ServiceLoader.load(ApiInterface.class);
    List<ApiInterface> list = new ArrayList<>();
    services.iterator().forEachRemaining(list::add);
    return list;
}
Run Code Online (Sandbox Code Playgroud)

api/src/module-info.java在行中添加uses com.foo.api.ApiInterface;但它给出了相同的结果(没有)。

我让它工作的唯一方法是将main从未命名模块迁移到命名模块。

1.当未命名模块试图与命名模块交互时,java 9如何工作?

2. 是否有可能让它工作并保持主要的未命名模块?

更新 1.3 -添加相关项目

And*_*sha 5

ServiceLoader::load照常工作,但还有其他事情。

[简短回答]

1. 未命名模块对命名模块的读取与命名模块相同,但命名模块无法访问未命名模块中的类型。

2.您正在尝试从非模块化 JAR 启动应用程序,因此您必须通过 显式解析所需的模块--add-modules com.foo.impl

请注意,您所需的模块必须位于模块图上(例如 add by --module-path)。

[更多细节]

1.有4种不同类型的模块:内置平台模块、命名模块、自动模块、 未命名模块,每种模块的命名与未命名模块不同

正如他们所写,未命名模块将所有其他模块视为与命名模块相同:

当然,所有其他模块都有名称,因此我们今后将这些模块称为命名模块。

未命名的模块读取所有其他模块。[...]

未命名的模块导出其所有包。[...] 但是,这并不意味着命名模块中的代码可以访问未命名模块中的类型。事实上,命名模块甚至不能声明对未命名模块的依赖。[...]

如果在命名模块和未命名模块中都定义了包,则未命名模块中的包将被忽略。

即使是自动模块也确实被命名为

自动模块是隐式定义的命名模块,因为它没有模块声明。

2. 本答案的第二部分

如果您编译非模块化代码或从非模块化 JAR 启动应用程序,则模块系统仍在发挥作用,并且由于非模块化代码不表达任何依赖项,因此它不会从模块路径解析模块。

因此,如果非模块化代码依赖于模块路径上的工件,则需要使用选项手动添加它们--add-modules。不一定是全部,只是那些你直接依赖的(模块系统将引入传递依赖) - 或者你可以使用ALL-MODULE-PATH(检查链接的帖子,它更详细地解释了这一点)。

这个@nullpointer 注释将会很有用

此外,模块解析仍然需要在启动期间解析 impl。要检查哪个,您还可以使用该--show-module-resolution标志。

  • 很好的答案:)正在考虑自己写一个,但是这个已经足够详细了。 (3认同)