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测试:
尝试更改为非最终成员但错误仍然相同
data object B : A {
@JvmStatic
@Suppress("NON_FINAL_MEMBER_IN_OBJECT")
open // remove final modifier
fun provider(): A = this
}
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。 “提供者构造函数”是一个公共的、无参数的构造函数。