Java 9中"使用"指令的目的是什么?

Mor*_*hai 12 java java-9 java-module module-info

Java的ServiceLoader类现在已经被正式编入Java语言.META-INF/services现在可以使用.而不是在寻找提供商

provides <spiClass> with <providerClass>
Run Code Online (Sandbox Code Playgroud)

我无法理解的是,uses在服务加载模块声明中的使用:

uses <spiClass>
Run Code Online (Sandbox Code Playgroud)

引用模块系统的状态

模块系统可以通过扫描模块工件中的类文件来识别ServiceLoader::load 方法的调用来识别服务的使用 ,但这既缓慢又不可靠.模块使用特定服务是该模块定义的一个基本方面,因此为了效率和清晰度,我们在模块的声明中用uses子句表示:

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;
}

为什么模块系统了解特定服务的使用是否至关重要,尤其是如何引入效率?是不是懒洋洋地加载服务?为什么服务加载器不能直接寻找提供商?

And*_*eas 6

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

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

  • @MouseEvent 你是问模块管理的内部优化吗?如果是这样,您应该查看源代码。难道您不能接受指定“uses”对系统有帮助并接受这一点吗? (3认同)

Ste*_*n C 6

为什么模块系统知道特定服务的使用是至关重要的......

因为依赖性解析. 模块系统的状态在示例中说出了引用文本上方的几行:

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

关键是反射用于进行实例化.它发生模块解析后......以及应用程序开始运行之后.

......特别是如何引入效率?

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

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

  • "打开"是关于一个模块制作零件.但"使用"是关于一个模块说它*需要*的东西.这就像"请求"和"提供"之间的区别. (2认同)
  • 我确实得到了你的问题.问题是你没有得到我的答案.`provide`语句不需要任何解析,大多数肯定不使用运行时反射...就像调用`Class :: forName`或`ServiceLoader :: load`一样. (2认同)
  • 使用`Class :: forName`来加载未通过其他方法解析的模块中的类将失败.因为班级不会加载.如果在编译时或模块加载时不知道类的名称,则不能依赖模块解析来解析依赖关系.您需要通过确保相关JAR位于类路径上来处理*old school*方式中的依赖关系. (2认同)
  • 是的......但如果模块(包含您要加载的资源)尚未解决,它将无法工作**.这就是你在这里缺少的:模块解析时间和运行时之间的区别.无论如何.我厌倦了这一轮,一轮又一轮的讨论.如果你不相信*我,去做一些实验.并且......问问自己......为什么一个非常聪明的人(比我聪明!)的团队会遇到设计和实施实际上不必要的机制的麻烦? (2认同)

Nic*_*lai 6

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

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

因此,对于正常工作的服务,提供者模块必须将其放入模块图中,即使它们不是初始模块中的传递要求.但是,模块系统如何识别需要哪些模块作为服务提供商?所有使用provides条款?那会有点太多了.不,只应解决实际需要的服务提供商.

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

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

如果使用标志启动基于服务的应用程序,则可以观察到此行为--show-module-resolution:

root monitor
monitor requires monitor.observer
[...]
monitor binds monitor.observer.beta
monitor binds monitor.observer.alpha
Run Code Online (Sandbox Code Playgroud)

模块监视器 绑定模块monitor.observer.alpha,monitor.observer.beta即使它不依赖于它们中的任何一个.

(行情来自模块系统的状态 ;强调我的.)