6.4 服务装载机
有时,您使用服务体系结构开发应用程序。有些平台鼓励使用这种方法,例如OSGi(http://osgi.org),它用于开发环境、应用程序服务器和其他复杂的应用程序。这样的平台远远超出了本书的范围,但是JDK还提供了一个加载服务的简单机制,我们在这里描述。Java平台模块系统很好地支持了这个机制,参见第二卷第9章。
通常,在提供服务时,程序希望给服务设计者一些自由,让他们知道如何实现服务的特性。还需要从多个实现中进行选择。ServiceLoader
类使加载符合公共接口的服务变得容易。
使用每个服务实例应该提供的方法定义接口(或者,如果您愿意,定义一个超类)。例如,假设您的服务提供加密。
package serviceLoader;
public interface Cipher
{
byte[] encrypt(byte[] source, byte[] key);
byte[] decrypt(byte[] source, byte[] key);
int strength();
}
例如,服务提供者提供一个或多个实现此服务的类
package serviceLoader.impl;
public class CaesarCipher implements Cipher
{
public byte[] encrypt(byte[] source, byte[] key)
{
var result = new byte[source.length];
for (int i = 0; i < source.length; i++)
result[i] = (byte)(source[i] + key[0]);
return result;
}
public byte[] decrypt(byte[] source, byte[] key)
{
return encrypt(source, new byte[] { (byte) - key[0] });
}
public int strength() { return 1; }
}
实现类可以在任何包中,不一定与服务接口在同一包中。它们中的每一个都必须有一个无参数构造函数。
现在,将类的名称添加到META-INF/services
目录中的一个文件中的UTF-8编码文本文件中,该文件的名称与完全限定的类名匹配。在我们的示例中,文件META-INF/services/serviceLoader.Cipher
将包含
serviceLoader.impl.CaesarCipher
在这个例子中,我们提供了一个实现类。您还可以提供多个类,稍后在其中进行选择。
完成此准备后,程序将按如下方式初始化服务加载程序:
public static ServiceLoader<Cipher> cipherLoader = ServiceLoader.load(Cipher.class);
这应该在程序中只执行一次。
服务加载程序的iterator
方法通过提供的所有服务实现返回迭代器。(有关迭代器的更多信息,请参见第9章。)最容易使用增强的for
循环遍历它们。在循环中,选择适当的对象来执行服务。
public static Cipher getCipher(int minStrength)
{
for (Cipher cipher : cipherLoader) // implicitly calls cipherLoad
{
if (cipher.strength() >= minStrength) return cipher;
}
return null;
}
或者,您可以使用流(参见第二卷第1章)来定位所需的服务。流方法生成ServiceLoader.Provider
实例流。该接口具有用于获取提供程序类和提供程序实例的方法type
和get
。如果您按类型选择一个提供者,那么您只需要调用type
,而不需要实例化任何服务实例。
public static Optional<Cipher> getCipher2(int minStrength)
{
return cipherLoader.stream()
.filter(descr -> descr.type() == serviceLoader.impl.CaesarCipher.class)
.findFirst()
.map(ServiceLoader.Provider::get);
}
最后,如果您愿意使用任何服务实例,只需调用findFirst
:
Optional<Cipher> cipher = cipherLoader.findFirst();
Optional
类将在第二卷第一章解释。
java.util.ServiceLoader<S> 1.6
static <S> ServiceLoader<S> load(Class<S> service)
创建用于加载实现给定服务接口的类的服务加载程序。Iterator<S> iterator()
生成一个延迟加载服务类的迭代器。也就是说,每当迭代器前进时都会加载一个类。Stream<ServiceLoader.Provider<S>> stream()
9
返回提供程序描述符流,以便可以延迟加载所需类的提供程序。Optional<S> findFirst()
9
查找第一个可用的服务提供程序(如果有)。
java.util.ServiceLoader.Provider<S> 9
Class<? extends S> type()
获取此提供程序的类型。S get()
获取此提供程序的实例。