5

我有三个模块:模块-a、模块-b、模块-c。模块-a 和模块-b 在引导层。我自己创建的模块-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 的服务。以下代码:

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

我的错误是什么?

4

2 回答 2

2

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

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

于 2017-09-20T06:26:30.703 回答
1

您的问题的根本原因是

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如何将服务提供者部署为模块类路径

于 2017-09-19T18:03:27.343 回答