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

时间:2017-09-19 15:10:17

标签: java java-9 java-module

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

模块-a有一个接口com.mod-a.Service,在我的模块信息中我有:

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

我的错误是什么?

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分: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了解如何在类路径上部署服务提供商 作为模块