繁体   English   中英

Java ServiceLoader 解释

[英]Java ServiceLoader explanation

我试图了解Java ServiceLoader的概念、工作机制和具体用例,但发现官方文档过于抽象和混乱。

首先,文档概述了服务服务提供者 服务是一组封装在 jar 存档(API 库)中的接口和抽象类。 服务提供者是一组实现或扩展 API 的类,打包在一个不同的 jar 文件(提供者库)中。


到目前为止一切顺利,但随后文档变得混乱。

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

那么实际上什么是Service typeProvider class呢? 我的印象是,服务类型是 API 库中的一个门面,提供者类是提供者库中这个门面接口的实现, ServiceLoader实际加载的类。 这个对吗? 但对我来说仍然没有多大意义,所有的组件是如何联系在一起的。

提供者类作为代理来决定提供者是否能够满足特定请求以及可以按需创建实际提供者的代码是什么意思? 在哪里不能定义统一类型? 基本上这一段都是令人困惑的,我想通过一个具体的例子来听听更容易理解的解释。


然后关于提供者配置文件......

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

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

这是否意味着对于服务类型为 org.foo.BarServiceType 的 API,在类路径中必须存在提供者 jar 以及实现此类型的类META-INF/services/org.foo.BarServiceType命名的提供者配置文件列出此提供者类,所有这些都可以由加载Classloader的同一个类加载器ServiceLoader ,以在 API 上查找和绑定提供者?

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

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


最后关于ServiceLoader

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

服务加载机制如何处理以下情况:存在多个提供者及其配置文件,或者存在提供者配置文件条目但没有类本身?

在日志框架之外还有哪些ServiceLoader的其他具体用例? 它在Java EESpringHibernate等流行框架的底层使用了多少? 使用松散耦合的 API 提供程序绑定的服务加载机制有哪些替代方案,或者是否存在?

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

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

这是否意味着对于服务类型为 org.foo.BarServiceType 的 API,在类路径中必须存在提供者 jar 以及实现此类型的类和 META-INF/services/org.foo.BarServiceType 命名的提供者配置文件列出此提供者类,所有这些都可以由加载 ServiceLoader 的同一个类加载器访问,以在 API 上查找和绑定提供者?

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

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

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

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

如果我正确理解您的问题,答案是实际上,SPI 描述符始终与它命名的提供程序类位于同一个 .jar 文件中。

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

服务加载机制如何处理以下情况:存在多个提供者及其配置文件,或者存在提供者配置文件条目但没有类本身?

如果类路径中存在多个提供程序,ServiceLoader 将简单地在其Iterator中返回所有这些提供程序。

对于不正确的配置文件,会从 ServiceLoader 的 Iterator 的 next() 方法中抛出ServiceConfigurationError 从文档中:

加载服务提供商时出现问题时抛出错误。

在以下情况下会抛出此错误:

  • 提供者配置文件的格式违反规范;
  • 读取提供者配置文件时发生 IOException;
  • 无法找到在提供者配置文件中命名的具体提供者类;
  • 具体的提供者类不是服务类的子类;
  • 无法实例化具体的提供者类; 要么
  • 发生某种其他类型的错误。

总之:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM