【问题标题】:JPMS ServiceLoader does not work for me as expectedJPMS ServiceLoader 无法按预期为我工作
【发布时间】:2021-10-11 22:45:33
【问题描述】:

JPMS ServiceLoader 无法正常工作。

我正在尝试提供一个带有默认服务的桌面程序作为可执行 jar,该服务可以由单个用户重载。用户提供自己的服务类,并在命令行中将其名称作为参数提供。

服务

package eu.ngong.myService;

public interface MyService {
    public String name();
    public void doSomething();
}

程序连同默认服务(插入iffor的第一行用于记录):

package eu.ngong.myService;

import java.util.ServiceLoader;

public class ServiceUser implements MyService {
    private static MyService myService = new ServiceUser();

    public static void main(String[] args) {
        if (args.length > 0) {
            System.out.println("trying to load " + args[0] + " envirionment.");
            ServiceLoader<MyService> myServices = ServiceLoader.load(MyService.class);
            for (MyService ms : myServices) {
                System.out.println(ms.name());
                if (ms.name().equalsIgnoreCase(args[0])) {
                    myService = ms;
                }
            }
        }
        myService.doSomething();
    }

    @Override
    public void doSomething() {
        System.out.println("The default service is acting.");
    }

    @Override
    public String name() {
        return "Default";
    }
}

两者都收集在 myService.jar 中,主类 ServiceUser 托管 module-info.java

module MyService {
    exports eu.ngong.myService;
    provides eu.ngong.myService.MyService with eu.ngong.myService.ServiceUser;
}

用户的个人罐子可能是

package eu.ngong.user1;
import eu.ngong.myService.MyService;
public class User1 implements MyService {

    @Override
    public String name() {
        return "User1";
    }

    @Override
    public void doSomething() {
        System.out.println("User1 is acting.");
    }

}

使用 module-info.java

module User1 {
    requires MyService;
    provides eu.ngong.myService.MyService with eu.ngong.user1.User1;
}

但是,运行程序与

java -p ..\user1\user1.jar;myService.jar -jar myService.jar User1

只导致意外输出

trying to load User1 environment.
The default service is acting.

虽然我期望使用日志记录

trying to load User1 environment.
Default
User1
User1 is acting.

我错过了什么?

【问题讨论】:

  • 如果你删除“-jar myService.jar”并直接用主类替换它:eu.ngong.myService.ServiceUser?而且,您是否添加了引用实现的META-INF/eu.ngong.myService.MyService
  • @NoDataFound -jar-m 应该非常相似。 META-INF/services 是一个很好的建议,但只要模块被认为是显式的,在我看来也可能不需要。
  • @NoDataFound:切换到 -m 是解决方案的一部分。引用Oracle:使用-jar时,指定的JAR文件是所有用户类的来源

标签: java java-platform-module-system serviceloader


【解决方案1】:
  1. 你需要声明

    uses eu.ngong.myService.MyService;
    

在要执行服务加载代码的模块中。

  1. 如果自动处理,请尝试确保添加了 META-INF/services 以通过 jar 文件正确解析模块。

由于这不适合 cmets,因此在此处分享类似示例的发现。通过 IntelliJ 执行时,我可以注意到该代码仅适用于 uses 声明和 user-service 模块的类路径。

我还尝试使用类似的命令行:

java --show-module-resolution -p base-service/target/classes:user-service/target/classes --add-modules base.service,user.service -m base.service/base.service.ServiceUser user1
root base.service file://.../base-service/target/classes/
root user.service file://.../user-service/target/classes/
user.service requires base.service file://.../base-service/target/classes/
base.service binds user.service file://.../user-service/target/classes/
...
trying to load user1 environment.
Services found : 2
User1
Default
User1 is acting.

并添加了--show-module-resolution 以查找为什么在模块路径上提供 jar 不起作用。输出如下,区别在于服务模块是not able to bind用户模块。

java --show-module-resolution -p base-service/target/base-service-1.0-SNAPSHOT.jar:user-service/target/user-service-1.0-SNAPSHOT.jar --add-modules base.service,user.service -m base.service/base.service.ServiceUser user1
root base.service file://...base-service/target/base-service-1.0-SNAPSHOT.jar automatic
root user.service file://...user-service/target/user-service-1.0-SNAPSHOT.jar
user.service requires base.service file://...base-service/target/base-service-1.0-SNAPSHOT.jar automatic
.....
trying to load user1 environment.
Services found : 1
User1
User1 is acting.

这也可能是您执行方式中出现意外输出的原因。对我来说,Default 实现的细微差别在后者中没有得到解决。

【讨论】:

  • 我要执行服务加载代码的模块中的module-info.java必须得到一个uses interface语句。 -- 关于ServiceLoader,在 META-INF 或 MANIFEST.MF 下没有任何内容。 module-info.class 包含将 jar 文件指定为 Java 模块所需的所有信息,甚至是使用服务或提供服务的模块。自动模块可能不同。
  • @ngong true,自动模块的行为似乎有所不同。这就是我要求this question 跟进的原因。与uses 相关的ServiceLoader API doc 我相信读得很清楚。
  • @ngong 我对你的评论感到困惑。你是对的,你不需要弄乱META-INFMANIFEST.MF,但这与你评论的开头有什么关系? uses 指令正是针对模块化软件,进入module-info
  • @Holger 我也对你的评论感到困惑。 module-info.class 进入 jar 文件。它与 META-INF 或 MANIFEST.MF 无关。我很高兴的是,我可以留在 Java 级别,维护 module-info.java,生成一个简单的 jar 文件,而不必担心从 Java 源获取信息到任何文本文件 (MANIFEST.MF) jar 文件。对我来说,这是对 JPMS 附带的 ServerLoader 技术的改进。
  • @ngong 我什么也没说。将服务加载器集成到语言中是一个很大的改进。但这并没有改变您的模块声明(您的问题中发布的版本)缺少uses 声明的事实。
猜你喜欢
  • 2013-02-14
  • 2013-12-23
  • 2014-12-09
  • 2016-01-13
  • 2020-09-21
  • 2011-08-17
  • 2012-04-29
  • 2021-08-12
  • 2019-02-04
相关资源
最近更新 更多