服务提供者Service Provider
概述
META-INF/services 目录中的文件是服务提供者配置文件。服务是一组众所周知的接口和(通常是抽象的)类。服务提供者是服务的具体实现。提供者中的类通常实现服务中定义的接口并继承这些类。服务提供者可以以扩展形式安装在 Java 平台的实现中,即放置在任何常见扩展目录中的 jar 文件。提供者也可以通过将它们添加到 applet 或应用程序类路径或通过其他特定平台的方式提供。
服务由一个抽象类表示。给定服务的提供者包含一个或多个具体类,这些类通过特定于提供者的数据和代码扩展此服务类。此提供者类通常不会是整个提供者本身,而是一个代理,它包含足够的信息来决定提供者是否能够满足特定请求,并且可以按需创建实际提供者的代码。提供者类的详细信息往往是高度服务特定的;没有单一的类或接口可以统一它们,因此没有定义这样的类。这里强制执行的唯一要求是提供者类必须有一个无参数构造函数,以便在查找期间可以实例化它们。
提供者配置文件
服务提供者通过在资源目录 META-INF/services 中放置提供者配置文件来识别自己。文件名应由抽象服务类的完全限定名组成。文件应包含由唯一具体提供者类名组成的换行分隔列表。空格和制表符以及空行将被忽略。注释字符是 ‘#’(0x23);在每行上,所有跟随第一个注释字符之后的字符将被忽略。文件必须以 UTF-8 编码。
示例
假设我们有一个名为 java.io.spi.CharCodec 的服务类。它有两个抽象方法:
public abstract CharEncoder getEncoder(String encodingName);
public abstract CharDecoder getDecoder(String encodingName);
每个方法返回一个适当的对象,如果无法翻译给定的编码则返回 null。典型的 CharCodec 提供者将支持多个编码。
如果 sun.io.StandardCodec 是 CharCodec 服务的提供者,则其 jar 文件将包含文件 META-INF/services/java.io.spi.CharCodec。该文件将包含单行:
sun.io.StandardCodec # 平台的标准编解码器
要为给定的编码名找到编码器,内部 I/O 代码将执行如下操作:
CharEncoder getEncoder(String encodingName) {
Iterator ps = Service.providers(CharCodec.class);
while (ps.hasNext()) {
CharCodec cc = (CharCodec)ps.next();
CharEncoder ce = cc.getEncoder(encodingName);
if (ce != null)
return ce;
}
return null;
}
提供者查找机制始终在调用者的安全上下文中执行。受信任的系统代码通常应该在特权安全上下文中调用此类中的方法。
更简单的解释
想象你在一个咖啡店里,菜单上有很多种类的饮品(服务)。每一种饮品可能由不同的咖啡师(提供者)来制作。每个咖啡师都有自己的独特配方和制作方法。
在这个例子中,服务就像是饮品的种类,而服务提供者就像是不同的咖啡师。META-INF/services 目录中的配置文件就像是咖啡店的菜单,它告诉你每种饮品是由哪位咖啡师提供的。
实际应用
假设你想要一杯拿铁(服务),你会先看菜单(服务提供者配置文件),找到“拿铁”这个选项(服务类 java.io.spi.CharCodec)。在这一行,你会看到“sun.io.StandardCodec”这个名字,表示这位咖啡师擅长制作拿铁(具体提供者)。
具体操作
当你点一杯拿铁时,咖啡店会依次询问每位咖啡师,看看谁能制作拿铁(遍历提供者)。一旦找到合适的咖啡师,他就会为你制作一杯拿铁(创建具体对象)。代码类似如下:
CharEncoder getEncoder(String encodingName) {
Iterator ps = Service.providers(CharCodec.class);
while (ps.hasNext()) {
CharCodec cc = (CharCodec)ps.next();
CharEncoder ce = cc.getEncoder(encodingName);
if (ce != null)
return ce;
}
return null;
}
这个过程确保你能得到一杯符合你要求的拿铁,就像在咖啡店里找到你喜欢的饮品一样。
希望这个比喻能让你更清楚地理解服务提供者配置文件的概念。