服务提供者在 jar 文件外部运行,但不在 jar 文件内部运行

问题描述 投票:0回答:1
import java.util.ServiceLoader

interface A

data object B : A {
    @JvmStatic
    fun provider(): A = this
}

fun main() {
    println(ServiceLoader.load(A::class.java).toList())
}

这个程序在独立运行时运行得很好:

java.exe -p <program path>;<kotlin stdlib path> -m testJARSPI/my.program.MainKt

但是在 jar 文件中运行时会产生错误:

java -jar <path to jar>

留言:

Exception in thread "main" java.util.ServiceConfigurationError: my.program.A: my.program.B Unable to get public no-arg constructor
        at java.base/java.util.ServiceLoader.fail(ServiceLoader.java:586)
        at java.base/java.util.ServiceLoader.getConstructor(ServiceLoader.java:679)
        at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1240)
        at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1273)
        at java.base/java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1309)
        at java.base/java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1393)
        at kotlin.collections.CollectionsKt___CollectionsKt.toCollection(_Collections.kt:1295)
        at kotlin.collections.CollectionsKt___CollectionsKt.toMutableList(_Collections.kt:1328)
        at kotlin.collections.CollectionsKt___CollectionsKt.toList(_Collections.kt:1319)
        at my.program.MainKt.main(Main.kt:15)
        at my.program.MainKt.main(Main.kt)
Caused by: java.lang.NoSuchMethodException: my.program.B.<init>()
        at java.base/java.lang.Class.getConstructor0(Class.java:3641)
        at java.base/java.lang.Class.getConstructor(Class.java:2324)
        at java.base/java.util.ServiceLoader$1.run(ServiceLoader.java:666)
        at java.base/java.util.ServiceLoader$1.run(ServiceLoader.java:663)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:569)
        at java.base/java.util.ServiceLoader.getConstructor(ServiceLoader.java:674)
        ... 9 more

我找不到 jar 文件中发生的更改

注意:

MANIFEST.MF
文件、
services
文件夹和
module-info.java
已正确配置。

使用 IntelliJ 构建:从模块创建 JAR -> 提取到目标 JAR

用jdk测试:

  • open-jdk 22
  • open-jdk 19(显示堆栈跟踪)
  • corretto 22

尝试更改为非最终成员但错误仍然相同

data object B : A {
    @JvmStatic
    @Suppress("NON_FINAL_MEMBER_IN_OBJECT")
    open // remove final modifier
    fun provider(): A = this
}
java kotlin jar jvm
1个回答
0
投票

每当

ServiceLoader
的文档谈论提供者方法1时,它总是在模块的上下文中。也许它可能更清楚,但含义是只有当在“非自动”命名模块中找到服务提供者时才会使用提供者方法。如果在类路径上找到服务提供者,则提供者方法(如果有)将被忽略;仅考虑提供者构造函数2 当您启动应用程序时:

java.exe -p <program path>;<kotlin stdlib path> -m testJARSPI/my.program.MainK

您的应用程序从模块路径加载并解析为命名模块。因此,
provider()

方法将按您的预期工作。

但是,当您使用以下方式启动应用程序时:

java -jar <path to jar>

您的应用程序将从类路径加载到未命名的模块中。因此,
provider()

方法被忽略。因此,它会查找提供者构造函数,该构造函数必须是“public”。但由于您的服务提供者是 Kotlin

object
,因此它的无参构造函数是 private。因此
NoSuchMethodException
如果您希望您的服务提供者是 Kotlin
object
,那么我相信您必须将其放在模块路径上,以便将其加载到命名模块中。如果您希望能够将代码放在类路径上,那么不要将服务提供者设为 Kotlin

object

。不过,如果您仍然想要单例,那么您可以通过使服务成为返回单例的“工厂”来引入一些间接方法。


1。 “提供者方法”是一个公共、静态、无参数的方法,名为 

provider

2。 “提供者构造函数”是一个公共的、无参数的构造函数。

© www.soinside.com 2019 - 2024. All rights reserved.