6

我喜欢服务。我也喜欢模块系统。对我来说不幸的是,在我使用 Java 9 之前,我养成了在运行时通过 jar 从加载的 jar 中获取服务提供者的习惯URLClassLoader,如下所示(为了简洁起见,我将使用 Java 10 var):

var url = new File("myjar.jar").toURI().toURL();
var cl = new URLClassLoader(new URL[] {url}, getClass().getClassLoader());
var services = ServiceLoader.load(MyService.class, cl);
for (var service : services) {
  ...
}

这工作正常,即使在 Java 9 及更高版本中,但它会将 jar 加载到类路径中,这意味着它使用旧META-INF\services方法来查找服务提供者。我宁愿使用该module-info方法,但这需要将 jar 加载到模块路径上,但我找不到任何可以这样做的方法。所以我在这里,希望这里有更彻底了解模块系统的人会告诉我如何做到这一点(或者它不能,如果是这样的话)。

4

2 回答 2

5

我可以组装的最小的工作示例是

var path = Path.of("myjar.jar");
var cl = new URLClassLoader(new URL[]{path.toUri().toURL()});
var mf = ModuleFinder.of(path);
var cfg = Configuration.resolve(mf, List.of(ModuleLayer.boot().configuration()), mf, Set.of());
var ml = ModuleLayer.defineModulesWithOneLoader(cfg, List.of(ModuleLayer.boot()), cl).layer();
var services = ServiceLoader.load(ml, MyService.class);
services.forEach(System.out::println);

假设myjar.jar是一个声明提供MyService实现的模块化 jar。

于 2019-01-17T10:48:04.990 回答
0

原来我使用了错误的命令行选项(我没有意识到我不应该使用java -jar模块化 jars)。使用正确的命令,@Holger 的答案有效,除了传递给的集合Configuration.resolve需要包含要加载的模块的所有名称,这很容易修复:

var path = Path.of("myjar.jar");
var cl = new URLClassLoader(new URL[]{path.toUri().toURL()});
var mf = ModuleFinder.of(path);
var cfg = Configuration.resolve(mf, List.of(ModuleLayer.boot().configuration()), mf, mf.findAll().stream().map(module -> module.descriptor().name()).collect(Collectors.toSet()));
var ml = ModuleLayer.defineModulesWithOneLoader(cfg, List.of(ModuleLayer.boot()), cl).layer();
var services = ServiceLoader.load(ml, MyService.class);
services.forEach(System.out::println);
于 2019-01-19T04:07:34.097 回答