阅读(723) (0)

鸿蒙OS ServiceLoader

2022-06-16 16:28:33 更新

ServiceLoader

java.lang.Object

|---java.util.ServiceLoader<S&

public final class ServiceLoader<S>
extends Object
implements Iterable<S>

一个简单的服务提供者加载工具。

服务是一组众所周知的接口和(通常是抽象的)类。 服务提供者是服务的具体实现。 提供者中的类通常实现服务本身中定义的类的接口和子类。 服务提供者可以以扩展的形式安装在 Java 平台的实现中,即将 jar 文件放置在任何常用的扩展目录中。 还可以通过将提供程序添加到应用程序的类路径或通过某些其他特定于平台的方式来使提供程序可用。

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

通过在资源目录 META-INF/services 中放置提供者配置文件来识别服务提供者。 该文件的名称是服务类型的完全限定二进制名称。 该文件包含具体提供程序类的完全限定二进制名称列表,每行一个。 每个名称周围的空格和制表符以及空白行将被忽略。 注释字符为'#'('\u0023', NUMBER SIGN); 在每一行中,第一个注释字符之后的所有字符都将被忽略。 该文件必须以 UTF-8 编码。

如果一个特定的具体提供者类在多个配置文件中被命名,或者在同一个配置文件中被命名不止一次,那么重复的将被忽略。 命名特定提供者的配置文件不必与提供者本身位于相同的 jar 文件或其他分发单元中。 必须可以从最初查询配置文件的同一个类加载器访问提供程序; 请注意,这不一定是实际加载文件的类加载器。

提供者的定位和实例化是惰性的,即按需。 服务加载器维护到目前为止已加载的提供程序的缓存。 迭代器方法的每次调用都会返回一个迭代器,它首先按实例化顺序产生缓存的所有元素,然后延迟定位并实例化任何剩余的提供者,依次将每个提供者添加到缓存中。 可以通过 reload 方法清除缓存。

服务加载程序总是在调用者的安全上下文中执行。 受信任的系统代码通常应该从特权安全上下文中调用此类中的方法以及它们返回的迭代器的方法。

此类的实例对于多个并发线程的使用是不安全的。

除非另有说明,否则将 null 参数传递给此类中的任何方法都将导致抛出 NullPointerException。

示例 假设我们有一个服务类型 com.example.CodecSet,它旨在表示某些协议的编码器/解码器对集合。 在这种情况下,它是一个具有两个抽象方法的抽象类:

 public abstract Encoder getEncoder(String encodingName);
 public abstract Decoder getDecoder(String encodingName);

如果提供者不支持给定的编码,每个方法都会返回一个适当的对象或 null。 典型的供应商支持不止一种编码。

如果 com.example.impl.StandardCodecs 是 CodecSet 服务的实现,那么它的 jar 文件还包含一个名为

 META-INF/services/com.example.CodecSet

该文件包含一行:

 com.example.impl.StandardCodecs    # Standard codecs

CodecSet 类在初始化时创建并保存单个服务实例:

 private static ServiceLoader<CodecSet> codecSetLoader
     = ServiceLoader.load(CodecSet.class);

为了找到给定编码名称的编码器,它定义了一个静态工厂方法,该方法遍历已知和可用的提供程序,仅在找到合适的编码器或用完提供程序时返回。

 public static Encoder getEncoder(String encodingName) {
     for (CodecSet cp : codecSetLoader) {
         Encoder enc = cp.getEncoder(encodingName);
         if (enc != null)
             return enc;
     }
     return null;
 }

getDecoder 方法的定义类似。

使用说明 如果用于加载提供程序的类加载器的类路径包含远程网络 URL,则在搜索提供程序配置文件的过程中将取消引用这些 URL。

此活动是正常的,尽管它可能会导致在 Web 服务器日志中创建令人费解的条目。但是,如果未正确配置 Web 服务器,则此活动可能会导致提供程序加载算法虚假失败。

当请求的资源不存在时,Web 服务器应返回 HTTP 404(未找到)响应。但是,有时 Web 服务器被错误地配置为在这种情况下返回 HTTP 200 (OK) 响应以及有用的 HTML 错误页面。当此类尝试将 HTML 页面解析为提供程序配置文件时,这将导致抛出 ServiceConfigurationError。此问题的最佳解决方案是修复错误配置的 Web 服务器以返回正确的响应代码 (HTTP 404) 以及 HTML 错误页面。

方法总结

修饰符和类型 方法 描述
IteratorS iterator() 延迟加载此加载程序服务的可用提供程序。
static <S> ServiceLoader<S> load(Class<S> service) 使用当前线程的上下文类加载器为给定的服务类型创建一个新的服务加载器。
static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) 为给定的服务类型和类加载器创建一个新的服务加载器。
static <S> ServiceLoader<S> loadInstalled(Class<S> service) 使用扩展类加载器为给定的服务类型创建一个新的服务加载器。
void reload() 清除此加载器的提供程序缓存,以便重新加载所有提供程序。
String toString() 返回描述此服务的字符串。
从接口 java.lang.Iterable 继承的方法
forEach, spliterator
从类 java.lang.Object 继承的方法
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

方法详情

reload

public void reload()

清除此加载器的提供程序缓存,以便重新加载所有提供程序。

调用此方法后,迭代器方法的后续调用将懒惰地从头开始查找并实例化提供程序,就像新创建的加载程序所做的那样。

此方法适用于可以将新提供程序安装到正在运行的 Java 虚拟机中的情况。

iterator

public IteratorS iterator()

延迟加载此加载程序服务的可用提供程序。

此方法返回的迭代器首先按实例化顺序生成提供程序缓存的所有元素。 然后它会延迟加载并实例化任何剩余的提供者,依次将每个提供者添加到缓存中。

为了实现惰性,解析可用提供程序配置文件和实例化提供程序的实际工作必须由迭代器本身完成。 因此,如果提供程序配置文件违反了指定格式,或者如果它命名了无法找到和实例化的提供程序类,或者如果实例化类的结果不能分配给服务类型,则它的 hasNext 和 next 方法可以抛出 ServiceConfigurationError ,或者在定位和实例化下一个提供程序时引发任何其他类型的异常或错误。 要编写健壮的代码,只需要在使用服务迭代器时捕获 ServiceConfigurationError。

如果抛出这样的错误,那么迭代器的后续调用将尽最大努力定位和实例化下一个可用的提供程序,但通常不能保证这样的恢复。

设计说明 在这些情况下抛出错误可能看起来很极端。 这种行为的基本原理是格式错误的提供程序配置文件(如格式错误的类文件)表明 Java 虚拟机的配置或使用方式存在严重问题。 因此,最好抛出错误而不是尝试恢复,或者更糟糕的是,静默失败。

此方法返回的迭代器不支持移除。 调用它的 remove 方法将导致抛出 UnsupportedOperationException。

指定者:

接口 IterableS 中的迭代器

返回:

延迟加载此加载器服务的提供程序的迭代器

load

public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader)

为给定的服务类型和类加载器创建一个新的服务加载器。

类型参数:

类型参数名称 类型参数描述
S 服务类型的类

参数:

参数名称 参数描述
service 表示服务的接口或抽象类
loader 用于加载提供程序配置文件和提供程序类的类加载器,如果要使用系统类加载器(或者,如果失败,则为引导类加载器),则为 null

返回:

一个新的服务加载器

load

public static <S> ServiceLoader<S> load(Class<S> service)

使用当前线程的上下文类加载器为给定的服务类型创建一个新的服务加载器。

调用表单的这种便捷方法

 ServiceLoader.load(service)

相当于

 ServiceLoader.load(service,
                    Thread.currentThread().getContextClassLoader())

类型参数:

类型参数名称 类型参数描述
S 服务类型的类

参数:

参数名称 参数描述
service 表示服务的接口或抽象类

返回:

一个新的服务加载器

loadInstalled

public static <S> ServiceLoader<S> loadInstalled(Class<S> service)

使用扩展类加载器为给定的服务类型创建一个新的服务加载器。

这个方便的方法只是简单的定位扩展类加载器,调用它extClassLoader,然后返回

 ServiceLoader.load(service, extClassLoader)

如果找不到扩展类加载器,则使用系统类加载器; 如果没有系统类加载器,则使用引导类加载器。

此方法旨在仅在需要已安装的提供程序时使用。 生成的服务只会查找并加载已安装到当前 Java 虚拟机中的提供程序; 应用程序类路径上的提供者将被忽略。

类型参数:

类型参数名称 类型参数描述
S 服务类型的类

参数:

参数名称 参数描述
service 表示服务的接口或抽象类

返回:

一个新的服务加载器

toString

public String toString()

返回描述此服务的字符串。

覆盖:

类 Object 中的 toString

返回:

描述性字符串