【问题标题】:Unnamed module interaction with named module by ServiceLoader::loadServiceLoader::load 与命名模块的未命名模块交互
【发布时间】:2018-01-18 18:21:20
【问题描述】:

我有一个这样的项目:

\---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 - 更新下面的代码见 cmets,添加 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

【问题讨论】:

  • module com.foo.impl 似乎缺少 requires com.foo.api 所以我怀疑它会编译。在任何情况下,您都需要使用 --add-moduels com.foo.impl 运行以确保解析 com.foo.impl 模块 - 您需要这个,因为没有模块声明需要它,也没有其他模块 uses com.foo.api
  • @Andrew 另外,当您迁移到命名模块时,您没有在主模块描述符中放置requires com.foo.api吗?
  • @nullpointer - 初始模块位于类路径中,因此它没有模块信息。混合模块路径和类路径是可以的,我怀疑他只是缺少--add-modules com.foo.impl
  • @AlanBateman 同意 OP 使用未命名模块的情况。该问题还提到了有关迁移到命名模块(模块路径)的问题。那是我询问模块描述符中使用的requires 指令的时候。
  • @AndrewChmielowski 我的意思是一样的(也许是讽刺的),你需要 impl 模块中的需求(如在你的编辑中)来编译未命名场景中的代码。此外,模块解析仍然需要在启动期间解析 impl。要检查您还可以make use of the --show-module-resolution 标记。

标签: java java-9 java-module module-info


【解决方案1】:

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 标记。

【讨论】:

  • 不错的答案 :) 本来想自己写一篇,但是这篇已经够详细了。
猜你喜欢
  • 1970-01-01
  • 2018-01-26
  • 1970-01-01
  • 1970-01-01
  • 2020-04-24
  • 2015-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多