使用ServiceLoader实现JRE服务,但没有模块

问题描述 投票:0回答:1

我们在Java 8下有一个非常大的项目。我暂时需要使用Java 9中引入的feature1来调试问题,所以我的计划是使用大多数当前的工具(可能是11.0.1)构建和运行,添加最少的调用该功能的代码,然后根本不将任何调试更改合并到主干。

面临的挑战是,我不想仅仅为此而构建一个module-info.java。 (现有的非模块代码实际上在Java SE 11下构建和运行没有任何问题,只是生产用户环境受限于Java 8.因此,我们几乎没有努力尝试模块化这个项目。)

为什么模块可能很重要:我需要使用的功能是System.LoggerFinder,它是通过ServiceLoader找到的。使用ServiceLoader的新方法是通过模块声明,这就是当前服务加载器文档花费大部分时间描述的内容。将文本文件放在META-INF/services/下的旧方法仍然有效,我们已经成功使用了这些服务和提供程序类,但当前的设置不适用于LoggerFinder

我们正在尝试加载的临时类

package com.example.for.stackoverflow;

public class LoggerFinder extends System.LoggerFinder
{
    public LoggerFinder()
    {
        System.err.println("behold, a LoggerFinder");
    }


    @Override
    public System.Logger getLogger (String name, Module module)
    {
        org.slf4j.Logger real = ...existing function to fetch logging facade...
        return new SystemLoggerWrapper (name, real);
    }
}

class SystemLoggerWrapper implements System.Logger { ... }

这种包装器实现非常简单,并已在其他地方发布。但是,已发布的示例都使用module-info.javaServiceLoader发送到DTRT。

这个项目的构建系统是用Ant完成的,所以我们在<jar>任务下添加了行:

<service type="java.lang.System.LoggerFinder">
  <provider classname="com.example.for.stackoverflow.LoggerFinder"/>
</service>

这是有效的,因为最终的JAR包含一个包含正确类名的META-INF/services/java.lang.System.LoggerFinder文本文件。

但是,在运行时,不使用提供的LoggerFinder。通过查看-verbose:class的输出,很明显默认值仍在使用中。 ServiceLoader本身没有例外。运行时类路径已经在接收LoggerFinder实现所在的JAR(它不是唯一的服务,并且正在找到其他ServiceLoader-esque提供程序)。

关于这个主题的问题,herehere的现有问题很少,但他们也采取模块化的方式。

有没有办法了解ServiceLoader正在做什么?这只是一个没有modules-info.java徒劳的练习吗?


1我正在尝试激活来自sun.util.logging.PlatformLogger系统的调试消息,该系统(至少从Java 9开始)将通过System.Logger工作,并且如果 - 根据自己的文档 - 可以通过ServiceLoader找到System.LoggerFinder。所有这些杂耍都是针对PlatformLogger的目标。

例如,https://www.baeldung.com/java-9-logging-api第3节

java
1个回答
1
投票

ServiceLoader在类路径上加载类的方式没有改变1。它仍然通过在META-INF/services下搜索适当的提供者配置文件来定位提供者。

LoggerFinder的文档说它搜索system class loader可见的提供者。正如您在评论中提到的那样,提供者是通过-cp包含的,这应该不是问题。

provider-configuration文件的名称必须是SPI的完全限定二进制名称。来自ServiceLoader文档:

Deploying service providers on the class path

通过将provider-configuration文件放在资源目录META-INF/services中来标识打包为类路径的JAR文件的服务提供程序。 provider-configuration文件的名称是服务的完全限定二进制名称。 provider-configuration文件包含服务提供者的完全限定二进制名称列表,每行一个。

LoggerFinder返回的Class.getName的二进制名称是java.lang.System$LoggerFinder。基于此,provider-configuration文件的名称应为:

META-INF/services/java.lang.System$LoggerFinder

我对Ant并不熟悉,但我猜你应该改变type的值来使用完全限定的二进制名称。

<service type="java.lang.System$LoggerFinder">
  <provider classname="com.example.for.stackoverflow.LoggerFinder"/>
</service>

注意:当我自己测试时,我最初无法让系统使用我的LoggerFinder实现。当我将提供者配置文件名更改为java.lang.System$LoggerFinder(最后一次读取文档)时,它按预期工作。不幸的是,我没有Ant可用于测试<jar>任务的解决方案。


1.除非你使用ServiceLoader.load(ModuleLayer,Class),否则它会忽略未命名的模块(即classpath)。

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