我有三个模块: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
我的错误是什么?
答案 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
了解如何在类路径上部署服务提供商 作为模块 和 强>