Java的ServiceLoader
类现已正式编入Java语言。而不是在META-INF/services
中查找提供程序,您现在可以使用
provides <spiClass> with <providerClass>
我无法理解的是,在服务加载模块声明中使用uses
:
uses <spiClass>
引自模块系统的状态
模块系统 可以识别的用途 通过扫描服务 模块中的类文件 调用的工件 的
ServiceLoader::load
方法,但那会 既慢又 不可靠的。那个 模块使用特定的 服务是根本 该模块的方面 定义,所以两者 效率和清晰度我们 在表达中表达 模块的声明 使用条款:module java.sql { requires transitive java.logging; requires transitive java.xml; exports java.sql; exports javax.sql; exports javax.transaction.xa; uses java.sql.Driver; }
为什么模块系统了解特定服务的使用是否至关重要,尤其是如何引入效率?是不是懒洋洋地加载服务?为什么服务加载器不能直接寻找提供商?
答案 0 :(得分:6)
为什么模块系统知道特定服务的使用是否很重要......
由于依赖性解析。 The State of the Module System在示例中说出了引用文字上方几行:
为了使
java.sql
模块能够使用此驱动程序,ServiceLoader
类必须能够通过反射实例化驱动程序类;要实现这一点,模块系统必须将驱动程序模块添加到模块图并解决其依赖关系......
关键是反射用于进行实例化。在模块解析之后发生...并且在应用程序开始运行之后。
......特别是如何引入效率?
扫描代码库以查找ServiceLoader::load
的所有来电都很昂贵。仅知道调用方法是不够的(可以通过分析类文件依赖性来完成)。您还需要知道参数的使用以确定要加载的类。而且(正如SotMS文件所指出的那样)会容易出错;例如如果参数是运行时表达式而不是编译时常量表达式。
他们采用的解决方案是提供一种显式声明对反射加载类的依赖的方法。
答案 1 :(得分:5)
当JVM启动时,模块系统resolves dependencies并构建模块图。只有运行时才能进入图表的模块(即使其他模块可以观察到)。如果通过服务正确地解耦模块,那么很可能提供模块不初始模块的传递依赖性。因此,如果没有进一步的努力,服务提供者模块通常不会进入模块图,因此在模块尝试使用服务时无法在运行时使用。
为了使
java.sql
模块能够使用此驱动程序,模块系统必须将驱动程序模块添加到模块图并解决其依赖关系[... ]
因此,要使服务正常工作,提供者模块必须将其放入模块图中,即使它们不是初始模块中的传递要求。但是,模块系统如何识别需要哪些模块作为服务提供商?所有使用provides
条款的人?那会有点太多了。不,只应解决实际需要的服务提供商。
这使得有必要识别服务使用。正如其他人所指出的,字节码分析速度慢且不可靠,因此需要更明确的机制来保证效率和正确性:uses
子句。只有使用它们,模块系统才能可靠,高效地使所有服务提供商模块可用。
如果应用程序是一个模块,那么它的模块声明必须有一个指定服务的
uses
指令;这有助于找到提供商,确保他们可靠地执行。
如果使用标记--show-module-resolution
root monitor
monitor requires monitor.observer
[...]
monitor binds monitor.observer.beta
monitor binds monitor.observer.alpha
模块监视器 绑定模块monitor.observer.alpha
和monitor.observer.beta
,即使它不依赖于其中任何一个。
(行情来自The State of the Module System;强调我的。)
答案 2 :(得分:3)
引自{9}的Java 9 javadoc(我强调):
应用程序通过调用ServiceLoader的静态
load
方法之一来获取给定服务的服务加载程序。如果应用程序是模块,则其模块声明必须具有指定服务的 uses 指令; 这有助于找到提供商并确保他们可靠地执行。此外,如果服务不在应用程序模块中,则模块声明必须具有 requires 指令,该指令指定导出服务的模块。