服务加载程序无法定位服务提供程序类,即使该类与META-INF / services位于同一个JAR文件中。

8
我成功地运行了一个C++应用程序,该应用程序使用JAR文件作为类路径参数加载JVM。然后,该应用程序成功地使用JNI调用来执行在此JAR文件中定义的.class文件中的各种函数。
.jar文件的目录结构中包含第三方一组.class文件-这些文件从jai_imageio.jar合并而来(使用Intellij IDEA将这些文件的完整目录结构合并到单个.jar文件中)。合并的.jar文件中还包括原始jai_imageio.jar的manifest.mf中的行-特别是implementation-title和相关行。另外,meta-inf/services文件夹也存在,并且也是从jai_imageio.jar中复制的。services目录中列出的各种服务看起来都是正确的。
特别地,在.jar文件中meta-inf/services文件夹中的javax.imageio.spi.ImageOutputStreamSpi包含单行com.sun.media.imageioimpl.stream.ChannelImageOutputStreamSpi,而在与该行指示的确切目录下的.jar文件中有相应的类:com/sun/media/imageioimpl/stream/ChannelImageOutputStreamSpi.class。
但是,当Java代码执行以下行时:
ImageIO.write(image, "tiff", file); // Assume 'image' is a BufferedImage and 'file' is a File

...它会抛出一个异常:

java.util.ServiceConfigurationError: javax.imageio.spi.ImageOutputStreamSpi:
Provider com.sun.media.imageioimpl.stream.ChannelImageOutputStreamSpi not found

即使在同一个.jar文件中,如上所述,该类确实存在,仍然出现此错误。请有人解释一下为什么会出现这个错误,以及我应该怎么做才能解决它。

1
不需要进行任何设置 - 您只需创建一个自包含的类,并使用 ImageIO.write(image, "tiff", file); 将其放在其中,然后尝试在classpath中运行该类及其jar文件。 - artbristol
1
整个“服务”机制在我看来有点不成熟,是Sun早期尝试依赖注入的一种方式,但实际上并没有起到作用(只需在OSGi或其他需要类加载器操作的上下文中尝试即可)。 - artbristol
你最后弄清楚这个问题的根本原因了吗?目前这是针对JNA的第72个问题,我大约一年前提出的。JNA也使用JNI,所以我的问题的原因可能和你的一样。 - Hakanai
在合并的JAR中,声明式注册服务的问题通常是因为一个JAR中的“META-INF / services”文件覆盖了另一个JAR中的文件。 - Tim Boudreau
你是说 com.sun.media.imageioimpl.stream.ChannelImageOutputStreamSpi 类被嵌入到你的 jar 包中了吗?我会期望它被嵌入到你的 JRE 中,如果在类路径上出现两次,可能会有潜在的类加载器问题。另外,META-INF/services 是一个区分大小写的路径,但你展示的是 meta-inf/services,这也会导致失败。 - ngreen
显示剩余4条评论
1个回答

3

根据这份文档http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html

"当一个线程被附加到虚拟机时,上下文类加载器是引导类加载器。"

如果通过AttachCurrentThread()方法将本地线程附加到JVM,则只会获取引导类加载器,甚至不会获取系统类加载器。除非您明确修复新线程的上下文类加载器,否则ServiceLoader引用的类将不可用。

可以按以下方式进行修复:

java.lang.Thread.currentThread().setContextClassLoader(
    java.lang.ClassLoader.getSystemClassLoader()
);

奇怪的是,当前版本的JNI文档中省略了这个相关细节。 - Wheezil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接