Tuo*_*nen 9 java frameworks jar classloader serviceloader
我试图理解 Java 的ServiceLoader概念、工作机制和具体用例,但发现官方文档太抽象和混乱。
首先,文档概述了服务和服务提供商。服务是一组接口和抽象类,封装在一个 jar 档案(API 库)中。服务提供者是一组实现或扩展 API 的类,封装在一个不同的 jar 文件(提供者库)中。
到目前为止一切顺利,但随后文档变得混乱。
出于加载的目的,服务由单一类型表示,即单一接口或抽象类。(可以使用具体类,但不建议这样做。)给定服务的提供者包含一个或多个具体类,这些类使用特定于提供者的数据和代码扩展该服务类型。提供者类通常不是整个提供者本身,而是一个代理,它包含足够的信息来决定提供者是否能够满足特定请求以及可以按需创建实际提供者的代码。提供者类的细节往往是高度特定于服务的;没有任何一个类或接口可以统一它们,所以这里没有定义这样的类型。
那么实际上Service type和Provider 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 库提供的工厂方法中?例如,不LoggingFactory.getLogger(clazz)的SLF4J内部委托给ServiceLoader,它使用反射来读取提供者配置文件并加载服务?
服务加载机制如何处理存在多个提供者及其配置文件的情况,或者有提供者配置文件条目但没有类本身的情况?
ServiceLoader日志框架之外的其他一些具体用例是什么?它在Java EE、Spring和Hibernate等流行框架中的应用程度如何?具有松散耦合 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 提供 ImageReaders,另一个 ImageReaderSpi 为 JPEG 提供 ImageReaders,依此类推。每个这样的服务提供者类(即 ImageReaderSpi 的每个具体实现)在其canDecodeInput方法中都有不同的逻辑,因此除非应用程序确实需要它们,否则不会创建重量级的 ImageReader 实例。
但是哪种方法更常见:在提供者之间提供配置文件,还是从 API 库本身提供列出一组支持的提供者的文件?
如果我正确理解您的问题,答案是在实践中,SPI 描述符始终与其命名的提供程序类位于同一个 .jar 文件中。
至于你问题的最后一部分:我认为记录器框架不使用 ServiceLoader。ServiceLoader的使用示例,查看Java SE所有以.spi(java.awt.im.spi、java.nio.channels.spi、java.nio.charset.spi等)结尾的包。他们中的大多数人没有说他们依赖 ServiceLoader,但他们都描述了他们的查找行为,你会发现它几乎总是与 ServiceLoader 的相同。
服务加载机制如何处理存在多个提供者及其配置文件的情况,或者有提供者配置文件条目但没有类本身的情况?
如果类路径中存在多个提供者,ServiceLoader 将简单地在其Iterator 中返回所有提供者。
对于不正确的配置文件,会从 ServiceLoader 的迭代器的 next() 方法抛出ServiceConfigurationError。从文档:
加载服务提供者时出现问题时抛出的错误。
在以下情况下会抛出此错误:
- 提供者配置文件的格式违反了规范;
- 读取提供者配置文件时发生 IOException;
- 找不到在提供者配置文件中命名的具体提供者类;
- 具体的提供者类不是服务类的子类;
- 一个具体的提供者类不能被实例化;或者
- 发生了一些其他类型的错误。
总之:
| 归档时间: |
|
| 查看次数: |
2962 次 |
| 最近记录: |