Java ServiceLoader解释

Java ServiceLoader explanation

我想了解JavaServiceLoader的概念、工作机制和具体用例,但发现官方documentation过于抽象和混乱。

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


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

For the purpose of loading, a service is represented by a single type, that is, a single interface or abstract class. (A concrete class can be used, but this is not recommended.) A provider of a given service contains one or more concrete classes that extend this service type with data and code specific to the provider. The provider class is typically not the entire provider itself but rather a proxy which contains enough information to decide whether the provider is able to satisfy a particular request together with code that can create the actual provider on demand. The details of provider classes tend to be highly service-specific; no single class or interface could possibly unify them, so no such type is defined here. The only requirement enforced by this facility is that provider classes must have a zero-argument constructor so that they can be instantiated during loading.

那么 服务类型 提供商 class 究竟是什么?我的印象是,服务类型是 API 库中的 facade,提供者 class 是提供者库中此外观接口的实现,即 class ServiceLoader 实际加载。这个对吗?但它对我来说仍然没有多大意义,所有的组件是如何联系在一起的。

提供者 class 作为代理来决定提供者是否能够满足特定请求以及可以按需创建实际提供者的代码是什么意思?在哪里不能定义统一类型?这段基本上都是乱七八糟的,想听个具体的例子解释一下。


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

A service provider is identified by placing a provider-configuration file in the resource directory META-INF/services. The file's name is the fully-qualified binary name of the service's type. The file contains a list of fully-qualified binary names of concrete provider classes, one per line ...

The configuration file naming a particular provider need not be in the same jar file or other distribution unit as the provider itself. The provider must be accessible from the same class loader that was initially queried to locate the configuration file; note that this is not necessarily the class loader from which the file was actually loaded.

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

上的提供商

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

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


终于约 ServiceLoader

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

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

ServiceLoader 在日志框架之外还有哪些其他具体用例?它在 Java EESpring 等流行框架中的使用情况休眠?松耦合 API--provider 绑定的服务加载机制有哪些替代方案?

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

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

Does this mean for an API with service type of org.foo.BarServiceType, in the classpath there must exists the provider jar with a class implementing this type and META-INF/services/org.foo.BarServiceType named provider configuration file listing this provider class, all accessible by the same Classloader which loaded ServiceLoader to find and bind the provider on the API?

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

why would it list multiple classes anyway if bundled?

有些服务提供商只能处理某些情况。一个例子是 IIORegistry class (which doesn’t mention ServiceLoader, and in fact was present long before ServiceLoader was added to Java SE, but functions identically to ServiceLoader). There might be one implementation of ImageReaderSpi which provides ImageReaders for PNG, another ImageReaderSpi which provides ImageReaders for JPEG, and so on. Each such service provider class (that is, each concrete implementation of ImageReaderSpi) would have different logic in its canDecodeInput 方法,因此不会创建重量级的 ImageReader 实例,除非应用程序确实需要它们。

But which approach is more common: to provide the configuration file among the provider, or provide the file listing a set of supported providers from within the API library itself?

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

至于你问题的最后一部分:我不认为记录器框架使用 ServiceLoader。 ServiceLoader的使用示例,查看Java SE所有以.spi结尾的包(java.awt.im.spi、java.nio.channels.spi、java.nio.charset.spi等)。大部分都没有说依赖ServiceLoader,但是都描述了自己的查找行为,你会发现几乎和ServiceLoader完全一样。

How does the service loading mechanism concerns situations, wheres there are either multiple providers with their configuration files present, or there is provider configuration file entry but not the class itself?

如果 class 路径中存在多个提供程序,ServiceLoader 将简单地 return 所有这些提供程序 Iterator

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

Error thrown when something goes wrong while loading a service provider.

This error will be thrown in the following situations:

  • The format of a provider-configuration file violates the specification;
  • An IOException occurs while reading a provider-configuration file;
  • A concrete provider class named in a provider-configuration file cannot be found;
  • A concrete provider class is not a subclass of the service class;
  • A concrete provider class cannot be instantiated; or
  • Some other kind of error occurs.

总结:

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