如何从 Java 9 中新创建的层中的模块调用服务?

How to call a service from module in a new created layer in Java 9?

我有三个模块:模块-a、模块-b、模块-c。 Module-a和module-b在引导层。 module-c 层我自己创建。

Module-a 有一个接口 com.mod-a.Service 并且在它的 module-info 中我有:

module module-a {
    exports com.mod-a;
}

Module-c 实现 com.mod-a.Service 并且在其模块信息中我有:

module module-c {
    requires module-a;
    provides com.mod-a.Service with com.mod-c.ServiceImpl;
}

Module-b与module-c新建层,并调用module-c服务。在它的模块信息中我有:

module module-b {
    requires module-a;
    requires java.management;
    requires slf4j.api;
    uses com.mod-a.Service;
}

在 module-b 中,我用 module-c 以这种方式创建新层:

ModuleFinder finder = ModuleFinder.of(moduleCPath);
ModuleLayer parent = ModuleLayer.boot();
Configuration cf = parent.configuration().resolve(finder, ModuleFinder.of(), Set.of("module-c"));
ClassLoader scl = ClassLoader.getSystemClassLoader();
ModuleLayer layer = parent.defineModulesWithOneLoader(cf, scl);
//the following line prints "module-c"
layer.modules().stream().map(Module::getName).forEach(System.out::println);

但是创建图层后无法在module-b中调用module-c的Service。以下代码:

Iterable<Service> it = ServiceLoader.load(Service.class);
System.out.println("LINE 1");
for (Service service : it) {
     System.out.println("Service was called");
     service.doIt();
}
System.out.println("LINE 2");

输出:

LINE 1
LINE 2

我的错误是什么?

你的问题的根本原因是

ServiceLoader.load(Service.class) 

这是

的替代品
ServiceLoader.load(Service.class, Thread.currentThread().getContextClassLoader())

最终没有找到 Service 的任何服务提供商。

我能够解决这个问题的一种方法是将服务提供商的包打开到拥有该服务的模块,如:

module module-c {
    requires module-a;
    provides com.mod-a.Service with com.mod-c.ServiceImpl;
    opens com.mod-c to module-a;
}

此外,建议通过 ServiceLoader 了解如何部署服务提供商 作为模块在类路径上.

ServiceLoader.load(Class) 使用 TCCL 作为起点来定位服务的服务提供者,而您的示例应该使用子层或替代任何 class 加载器class 加载器在层中定义模块。因此,如果您将示例更改为 ServiceLoader.load(layer, Service.class),那么它应该会按预期工作。

另外,您使用了 resolve 并将服务提供者模块指定为要解析的根模块。这没有错,但替代方案是使用 resolveAndBind 并且不指定任何根模块。 module-b 中的 uses com.mod-a.Service 将确保 provides com.mod-a.Service 的模块将被解析。