将ServiceLoader迁移到Java 9模块系统

很久很久以前,我写了一篇有关ServiceLoader 。 简而言之,Service Loader允许在不同的JAR中分离API及其实现。 客户端代码仅取决于API,而在运行时,将使用类路径上的实现。 这是将客户端代码与实现代码分离的好方法。

例如,SLF4J使用ServiceLoader :在编译时将slf4j-api添加到类路径上,而可以在运行时在类路径上设置任何单个实现( 例如 slf4j-simplelogback )。 这是服务加载程序用法的典型子,在合同及其实现之间清晰地分开。

一个样本项目

为了说明这一点,让我们实现自己的日志记录项目:

  1. 记录API
    LogService.java
    publicinterfaceLogService{
        voidlog(Stringmessage);
    }
  2. 在stdout上编写的单个实现
    LogStdOut.java
    publicclassLogStdOutimplementsLogService{
        @Override
        publicvoidlog(Stringmessage){
            System.out.println(message);
        }
    }
  3. 调用API并利用服务加载程序机制的客户端
    客户端程序
    publicclassClient{
    
        publicstaticvoidmain(String[]args){
            ServiceLoader<LogService>loader=ServiceLoader.load(LogService.class);
            for(LogServiceservice:loader){
                service.log("Log written by "+service.getClass());
            }
        }
    }

之所以如此神奇,是因为客户端包含一个满足一些约束的服务加载器配置文件:

  1. 它位于/META-INF/services
  2. 它的名称是接口的标准名称
  3. 它包含实现类的标准类名:
    /META-INF/services/ch.frankel.blog.serviceloader.log.LogService
    ch.frankel.blog.serviceloader.log.stdout.LogStdOut

迁移到Java平台模块系统

如我之前所写,并非总是可以将不重要的应用程序迁移到Java平台模块系统。 从那时起,我认为没有太大变化。 但是,模块系统本身背后的概念非常简单。

关于我们的示例项目,需要执行以下步骤。

模块化API

为了使其他模块(实现和客户端)使用API​​,需要导出包含LogService接口的软件包。

module-info.java
modulelog.api{
    exportsch.frankel.blog.serviceloader.log;
}
模块化客户端

客户端位于模块依赖关系树的边界。 它只需要API模块。

module-info.java
modulelog.client{
    requireslog.api;
}
模块化实施

实现需要API。 它还应该导出包含实现的包, 以便可以在其他模块中使用

但是,这还不够。 从META-INF/services文件夹到特定于模块的实现,Java 9取代了Service Loader的工作方式。

为此,该模块信息的语法提供两个关键字: provides参考接口和with指定执行:

module-info.java
importch.frankel.blog.serviceloader.log.LogService;
importch.frankel.blog.serviceloader.log.stdout.LogStdOut;

modulelog.stdout{
    requireslog.api;
    exportsch.frankel.blog.serviceloader.log.stdout;
    providesLogService
        withLogStdOut;
}

有趣的是,仅配置发生了变化:客户端中的Service Loader代码本身没有变化。

结论

竖锯使继续获得服务加载程序的好处成为可能。 当配置更改时-从META-INF/services文件夹到已编译的module-info ,API保持不变。 迁移到模块系统可能会遇到一些困难,但Service Loader不应是其中之一。

这篇文章的完整源代码可以在Github上找到。

翻译自: https://blog.frankel.ch/migrating-serviceloader-java-9-module-system/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值