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

时间:2018-01-18 18:21:20

标签: 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

UnnamedStart.java的实施是:

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

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

api/src/module-info.java是:

module com.foo.api {
     exports com.foo.api;
}

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)
}

当我在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;
}

并添加api/src/module-info.javauses com.foo.api.ApiInterface;,但它给出了相同的结果(没有)。

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

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

2。是否可以使其工作并保持 main 像未命名的模块一样?

更新1.3 - added related project

1 个答案:

答案 0 :(得分:3)

ServiceLoader :: load 像往常一样工作,但还有其他事情。

[简答]

1。 未命名的模块命名模块读取到命名模块,但命名模块无法访问未命名模块中的类型。

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

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

[更多详情]

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

分开命名

As they wrote 未命名模块将所有其他模块视为命名模块

  

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

     

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

     

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

     

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

即使自动模块也确实是named

  

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

2。 Second part of this answer

  

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

     

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

这个@nullpointer评论很有用

  

此外,模块分辨率仍然需要在启动期间解决impl。要检查哪个也可以make use of the --show-module-resolution标记。