2017-07-29 42 views
0

我想了解Java的ServiceLoader的概念,工作机制和具体的使用案例,但找到官方documentation太抽象和混乱。Java服务加载器说明

首先,文件概述了服务服务提供商。 服务是一组打包在jar文件(API库)中的接口和抽象类。服务提供者是一组实现或扩展API的类,它们封装在一个独特的jar文件(提供程序库)中。


到目前为止好,但随后文档变得混乱。

为了加载,服务由单一类型表示,即单个接口或抽象类。 (可以使用具体类,但不建议这样做。)给定服务的提供者包含一个或多个具体类,这些类可以扩展此服务类型,并提供特定于提供者的数据和代码。提供者类通常不是整个提供者本身,而是包含足够信息的代理,以确定提供者是否能够满足特定请求以及可以按需创建实际提供者的代码。提供者类的细节往往是高度服务特定的;没有一个类或接口可以统一它们,所以这里没有定义这种类型。该工具强制执行的唯一要求是提供程序类必须具有零参数构造函数,以便在加载期间可以对其进行实例化。

那么究竟是服务类型Provider类?我得到的印象是,服务类型是API库中的facade,提供者类是提供程序库中实现此Facade接口的实例,实际上这个类是ServiceLoader加载的。它是否正确?但对我来说,它仍然没有什么意义,所有的组件如何连接在一起。

提供者类是一个代理,它决定提供者是否能够满足特定请求以及可以按需创建实际提供者的代码?没有统一的类型可以定义在哪里?基本上所有这一段都是令人困惑的,我希望通过一个具体的例子来听取更多可理解的解释。


然后约供应商配置文件 ...

服务提供商通过将供应商的配置文件中的资源目录META-INF /服务标识。该文件的名称是服务类型的完全限定二进制名称。该文件包含具体提供程序类的完全限定二进制名称列表,每行一个 ...

命名特定提供程序的配置文件不需要与提供程序本身位于同一个jar文件或其他分发单元中。提供者必须可以从最初查询的相同类加载器访问以查找配置文件;请注意,这不一定是实际加载文件的类加载器。

这是否意味着服务类型为org.foo的API。BarServiceType,类路径中必须存在提供者jar,其中包含实现此类型的类META-INF/services/org.foo.BarServiceType命名的提供程序配置文件列出了此提供程序类,它们都可以通过相同的Classloader访问,其中哪些代码可以加载ServiceLoader以在API上查找和绑定提供程序?

从类加载器的角度来看,可访问意味着提供程序配置文件和提供程序库可以在程序包之外提供,位于层次结构的上方,即来自容器或其他中间件。

提供程序配置文件列出提供程序类,并可能捆绑在提供程序包中(为什么它会列出多个类,如果捆绑?)或来自外部。但是更常见的方法是:在提供者中提供配置文件,还是从API库本身提供列出一组受支持的提供者的文件?或者后者是一种误解?


最后约ServiceLoader

ServiceLoader实际实例化,并呼吁加载服务提供商?这是否发生在由API库提供的工厂方法中?例如,LoggingFactory.getLogger(clazz)SLF4J内部委托给ServiceLoader,它使用反射来读取提供者配置文件并加载服务?

服务加载机制如何处理情况,有多个提供者的配置文件存在,还是存在提供程序配置文件条目,但不包含类本身?

除了日志框架之外还有其他一些ServiceLoader的具体用例吗?它在多大程度上被广泛使用在流行的框架之下,如Java EE,Spring and Hibernate?有什么替代服务加载机制与松散耦合的API提供程序绑定,或者有?

回答

2

服务类型是传递给ServiceLoader.load或ServiceLoader.loadInstalled的接口或抽象类。提供者是该接口或抽象类的具体实现。

由于服务通常由大量的功能组成,因此如果在ServiceLoader扫描这些大类时并不是所有这些大类都立即加载的话。相反,更好的设计是一个可以访问主要功能的小型类。例如,ServiceLoader.load(FileSystemProvider.class)不加载能够处理特定文件系统集的整个库;相反,它加载一个FileSystemProvider对象,该对象能够初始化该库,当且仅当该应用程序选择使用它时。这允许提供者本身保持轻量级。

这是否意味着与org.foo.BarServiceType的服务类型的API,在类路径中有一定存在一类供应商罐子实现这种类型和META-INF /服务/ org.foo.BarServiceType命名提供程序配置文件列出了这个提供程序类,所有这些都可以通过加载ServiceLoader在API上查找和绑定提供程序的同一个Classloader访问?

是的。通常这很简单;例如,包含org.foo的.jar文件。BarServiceType类还包含一个META-INF/services/org.foo.BarServiceType条目,其内容由一行文本组成。

为什么它会列出多个类,如果捆绑?

某些服务提供商只能处理某些情况。一个例子是IIORegistry类(它没有提到ServiceLoader,事实上,在ServiceLoader被添加到Java SE之前,它的功能与ServiceLoader的功能相同)。可能有ImageReaderSpi的一个实现,它为PNG提供ImageReader,为ImageReader提供JPEG的另一个ImageReaderSpi等等。每个这样的服务提供者类(即ImageReaderSpi的每个具体实现)在其canDecodeInput方法中将具有不同的逻辑,因此除非应用程序实际需要它们,否则不会创建重量级ImageReader实例。

但是哪种方法更为常见:在提供程序中提供配置文件,还是从API库本身提供列出一组受支持的提供程序的文件?

如果我正确理解你的问题,答案是在实践中,SPI描述符总是与它命名的提供程序类在同一个.jar文件中。

至于你的问题的最后部分:我不认为记录器框架使用ServiceLoader。有关使用ServiceLoader的示例,请查看以.spi(java.awt.im.spi,java.nio.channels.spi,java.nio.charset.spi等)结尾的所有Java SE软件包。他们中的大多数人并没有说他们依赖于ServiceLoader,但他们都描述了他们的查找行为,并且您会发现它几乎总是与ServiceLoader相同。

服务加载机制如何处理情况,是否存在多个提供程序及其配置文件存在,或者存在提供程序配置文件条目但不包含类本身?

在类路径中存在多个提供程序的情况下,ServiceLoader将简单地返回其所有Iterator中的所有提供程序。

对于错误的配置文件,从ServiceLoader的Iterator的next()方法引发ServiceConfigurationError。从文档:

加载服务提供商时出现错误时引发错误。

该错误将在以下情况下被抛出:

  • 提供者配置文件的格式违反规范;
  • 读取提供程序配置文件时发生IOException;
  • 无法找到提供程序配置文件中指定的具体提供程序类;
  • 具体的提供者类不是服务类的子类;
  • 一个具体的提供者类不能被实例化;或
  • 发生其他一些类型的错误。

总结:

  • 服务提供商实现类通常是相同的.jar的META-INF /服务描述符文件。
  • 服务提供者类通常是一个轻量级类,它允许应用程序访问更重的类,当且仅当应用程序决定需要它们时。
  • Java SE的很多部分都使用ServiceLoader,尤其是* .spi包。
  • 如果服务有很多实现,所有都在迭代器中返回。
  • 不正确的描述符导致ServiceConfigurationError。
+0

真的很有帮助。但我仍然发现一件令人困惑的事情:“服务提供者类通常是一个轻量级类,它允许应用程序访问更重的类,当且仅当应用程序决定需要它时”。这是否仅仅意味着它加载的类是完整API的外观,还是'proxy'在这里有更多的动态行为 –

+0

它既不是façade也不是代理。这有点像工厂。例如,[XPathFactory](https://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html)不是[XPath]的外观或代理(https:/ /docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPath.html)实例;它是获取XPath实例的手段。 – VGR