使用 java serviceloader build with jdk8 use in >= java9

Use java serviceloader build with jdk8 use in >= java9

我有两个罐子。一个提供服务接口和服务加载class,一个提供该服务的实现。

这在 jdk8 中 运行 时完美运行,但在 jdk9 或更高版本中 运行 时出现 service type not accessible to unnamed module @3754a4bf 错误。

我将这两个 jar 迁移到一个模块基础 jar,当 运行 在 >= jdk9 上时效果很好,但在 jdk8 上失败了,因为 class 文件版本错误。

所以,我在使用 java serivceloader api 和 java 8 或 9 时没有问题。

我知道 https://blog.codefx.org/tools/multi-release-jars-multiple-java-versions/ ,但我想避免使构建过程更加复杂。该构建已经涉及 class 搬迁和 其他东西。

我的问题:有没有办法使用 java 服务加载 api 在 jdk8 和 >=jdk9 上使用相同的 jars 运行?

在 JDK9 及更高版本中,所有 jar 都是有效的 'modules'。如果他们 没有 有一个模块定义(你通过创建一个名为 module-info.java 的文件并在其中放置一个模块声明来创建),它的名称是 'unnamed module @whatever',它导出它的所有包,并且可以访问任何其他模块导出的任何内容(用模块的话说:它 'reads' 一切)。意思是,如果 all 你的 classpath 依赖项是这样的未命名模块,它们都导出所有内容并且它们都读取所有内容因此它们都可以从任何人访问任何标记为 public 的内容else – 这就是它在 JDK8 中的工作方式,因此也是所有兼容的原因。

要清楚:在JDK9中,模块(jar)A中的代码访问方法,class,或模块(jar)B中的字段,那么除了通常的访问修饰符( public关键字),B需要'export'那个包,A需要读那个模块,不然不行。如果你没有明确地为任何一方编写模块信息文件,那么你会得到默认行为,即 'export everything' 和 'read everything',让我们回到 JDK8 场景:事情必须是标记为public,然后就可以访问了。

实际上我不建议你制作那个模块信息文件;这是一大步,您只有在熟悉模块系统后才能迈出这一步。

错误的意思是'service type'[1]不是'accessible'[2]到'unnamed module @3754a4bf'[3]。让我们把它分成几个部分:

[1] serviceloader 通过定义一个 'service provider' 实现的接口来工作,然后 class 使用服务加载器以该接口的一堆实例结束;每个代表一个实现。这是 ServiceLoader.load(Foo.class) 中的 Foo

[2] 'accessible' 是模块代言:需要这个的代码没有明确地 'read' 它,或者有这个的代码没有导出它。

[3] 'unnamed module@3754abf' 是试图访问它的模块的名称。

综上所述,这意味着:您在错误的假设下运行:无论 jar 包含您的服务接口(Foo 我在说什么),都 不是 一个未命名的模块,或者,可能不是 public。请注意,如果它是非 public class(即:/* package private */ class Example { public interface MyServiceInterface {} })内的 public 接口,则可能仍算作 'not public enough'.

如果是命名模块(也就是说:它有一个module-info.java文件),然后导出服务接口所在的包。见任意拼图(java模块的名称系统)关于如何设置它的教程。如果不是,请确保它是 public 接口或抽象 class,如果它在其他类型中,请确保它们也是 public。如果两者都不是,请检查您的 class 路径; javac 相当坚持这两种情况之一。