转载

将ServiceLoader迁移到Java 9模块系统 - frankel

Service Loader允许在不同的JAR中分离API及其实现。客户端代码仅取决于API,而在运行时,将使用类路径上的实现。这是将客户端代码与实现代码分离的好方法。

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

<b>public</b> <b>interface</b> LogService {
    <b>void</b> log(String message);
}

<b>public</b> <b>class</b> LogStdOut implements LogService {
    @Override
    <b>public</b> <b>void</b> log(String message) {
        System.out.println(message);
    }
}

调用API并利用服务加载程序机制的客户端:

<b>public</b> <b>class</b> Client {

    <b>public</b> <b>static</b> <b>void</b> main(String[] args) {
        ServiceLoader<LogService> loader = ServiceLoader.load(LogService.<b>class</b>);
        <b>for</b> (LogService service : loader) {
            service.log(<font>"Log written by "</font><font> + service.getClass());
        }
    }
}
</font>

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

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

迁移到Java平台模块系统

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

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

module-info.java

module log.api {
    exports ch.frankel.blog.serviceloader.log;
}

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

module-info.java

module log.client {
    requires log.api;
}

3. 模块化实施: 实现需要API。它还应该导出包含实现的包,以便可以在其他模块中使用。但是,这还不够。Java 9取代了Service Loader的工作方式,从META-INF/services文件夹到特定于模块的实现。

为此,module-info语法提供了两个关键字:使用provides引用接口并使用with指定实现:

module-info.java

<b>import</b> ch.frankel.blog.serviceloader.log.LogService;
<b>import</b> ch.frankel.blog.serviceloader.log.stdout.LogStdOut;

module log.stdout {
    requires log.api;
    exports ch.frankel.blog.serviceloader.log.stdout;
    provides LogService
        with LogStdOut;
}

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

原文  https://www.jdon.com/53281
正文到此结束
Loading...