【问题标题】:What's the purpose of 'uses' directive in Java 9?Java 9 中“uses”指令的目的是什么?
【发布时间】:2017-08-20 02:35:59
【问题描述】:

Java 的ServiceLoader 类现已正式融入Java 语言。您现在可以使用

,而不是在 META-INF/services 中寻找提供者
provides <spiClass> with <providerClass>

我不明白的是,uses在服务加载模块声明中的使用:

uses <spiClass>

引用The State of the Module System

模块系统 可以识别用途 通过扫描服务 模块中的类文件 调用的工件 的 ServiceLoader::load 方法,但那会 既慢又慢 不可靠。那一个 模块使用特定的 服务是根本 该模块的方面 定义,所以对于两者 我们的效率和清晰度 表示在 模块声明 带有uses子句:

模块 java.sql {
   需要传递 java.logging;
   需要传递 java.xml;
   导出java.sql;
   导出 javax.sql;
   导出 javax.transaction.xa;
   使用 java.sql.Driver;
}

为什么模块系统知道特定服务的用途是基础,尤其是这将如何提高效率?服务不是延迟加载的吗?为什么服务加载器不能即时寻找提供者?

【问题讨论】:

  • “兼顾效率和清晰度”

标签: java java-9 java-module module-info


【解决方案1】:

当 JVM 启动时,模块系统 resolves dependencies 并构建模块图。只有进入图表的模块在运行时可用(即使其他模块是可观察的)。如果模块通过服务正确解耦,那么提供模块很有可能是初始模块的传递依赖项。因此,如果没有进一步的努力,服务提供者模块通常不会进入模块图,因此在模块尝试使用服务时在运行时不可用。

为了让java.sql 模块使用此驱动程序 [...],模块系统必须将驱动程序模块添加到模块图并解决其依赖关系 [...] .

因此,为了使服务正常工作,提供者模块必须将其放入模块图中,即使它们不是从初始模块传递过来的。但是模块系统如何识别需要哪些模块作为服务提供者呢?所有这些都使用provides 子句?那有点过分了。不,只应解决实际需要的服务提供者。

这使得有必要识别服务用途。正如其他人所指出的,字节码分析缓慢且不可靠,因此需要更明确的机制来保证效率和正确性:uses 子句。只有有了它们,模块系统才能可靠高效地使所有服务提供者模块可用。

如果应用程序是一个模块,那么它的模块声明必须有一个指定服务的uses 指令;这有助于找到提供者并确保他们能够可靠地执行

如果使用标志--show-module-resolution 启动a service-based application,您可以观察到这种行为:

root monitor
monitor requires monitor.observer
[...]
monitor binds monitor.observer.beta
monitor binds monitor.observer.alpha

模块 monitor 绑定模块 monitor.observer.alphamonitor.observer.beta,即使它不依赖于其中任何一个。

(引用来自The State of the Module System;强调我的。)

【讨论】:

  • 终于有明确的答案了。但正如我已经问过@StephenC;如果是这样,当类作为系统属性传递时,如何加载具有反射的类,其名称直到运行时才知道?当然模块系统不会扫描Class::forName
  • 另一个问题。提供者不是总是需要服务模块吗?它必须实现该模块中包含的接口,不是吗?将其添加到图表中还不够吗?
  • 糟糕,错过了您的 cmets。关于反思的第一个问题:模块系统希望保证服务提供商将其纳入图表。它不希望使用Class::forName 进行反射访问。如果存在具有该类的模块,则可以;否则,那就是你倒霉了——模块系统根本不在乎。
  • 关于定义服务类型的模块的依赖关系的第二条评论:提供和使用服务的模块都需要该模块(provider -> serviceconsumer - > 服务)。模块系统遇到consumer时会在graph中添加service,但为什么会添加consumer呢? service -> consumer 就是这种情况,但从来都不是这样。在解决过程中是否应该因为有人需要服务而突然转向另一个方向?
【解决方案2】:

为什么模块系统知道特定服务的使用是基础...

因为依赖解析。 The State of the Module System 在示例中在您引用的文本上方几行表示:

为了让java.sql 模块使用这个驱动,ServiceLoader 类必须能够通过反射实例化驱动类;为此,模块系统必须将驱动模块添加到模块图中并解决其依赖关系...

关键是反射是用来做实例化的。它发生在 模块解析之后......并且在应用程序开始运行之后。

...尤其是这将如何提高效率?

扫描代码库以查找对 ServiceLoader::load 的所有调用是昂贵的。仅仅知道调用了一个方法是不够的(这可以通过分析类文件依赖关系来完成)。您还需要知道使用了哪些参数来确定要加载哪些类。并且(正如 SotMS 文件指出的那样)可能会出错;例如如果参数是运行时表达式而不是编译时常量表达式。

他们采用的解决方案是提供一种方法来显式声明对反射加载的类的依赖。

【讨论】:

    【解决方案3】:

    引用ServiceLoader 的 Java 9 javadoc(重点由我添加):

    应用程序通过调用 ServiceLoader 的静态 load 方法之一来获取给定服务的服务加载器。如果应用程序是一个模块,那么它的模块声明必须有一个指定服务的uses指令; 这有助于找到提供商并确保他们能够可靠地执行。此外,如果服务不在应用程序模块中,则模块声明必须有一个 requires 指令,指定导出服务的模块。

    【讨论】:

    • 如果我知道谁在使用它,加载提供程序的效率如何?用provides 指令声明它是提供者并让服务加载器查找它还不够吗?
    • @MouseEvent 你问的是模块管理的内部优化吗?如果是这样,您应该查看源代码。你不能接受指定uses 对系统有帮助,然后继续这样做吗?
    • 它说没有uses,它必须扫描ServiceLoader::load的使用。为什么?
    猜你喜欢
    • 1970-01-01
    • 2011-11-12
    • 1970-01-01
    • 2011-02-17
    • 2012-10-05
    • 2020-11-12
    • 1970-01-01
    相关资源
    最近更新 更多