SPI英文为Service Provider Interface 是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。
java中实现spi的主要类为 ServiceLoader,一个简单的服务提供商加载工具。 一个服务是一组众所周知的接口(通常是抽象的)类。 服务提供商是服务的具体实现。 提供者中的类通常实现接口并对服务本身定义的类进行子类化。 服务提供商可以以Java扩展的形式安装在Java平台的实现中,也就是将jar文件放置到任何通常的扩展目录中。 提供商也可以通过将它们添加到应用程序的类路径或某些其他平台特定的方式来提供。
java 中通过实现接口,通过基于接口的编程模式,将服务规范与服务提供相分离,实现不同的厂商提供服务
要使用Java SPI,需要遵循如下约定:
服务由单一类型表示,即单个接口或抽象类。
public interface IPrintServiceSPI {
/**
* 打印对象
* @param o
*/
void print(Object o);
}
public class JavaPrintServiceSPI implements IPrintServiceSPI{
@Override
public void print(Object o) {
System.out.println(getClass().getSimpleName()+":"+o.toString());
}
}
public class JSONPrintServiceSPI implements IPrintServiceSPI {
@Override
public void print(Object o) {
System.out.println(getClass().getSimpleName()+":"+JSON.toJSONString(o));
}
}
public static void main(String[] args) {
ServiceLoader<IPrintServiceSPI> serviceLoader = ServiceLoader.load(IPrintServiceSPI.class); //加载服务
Iterator<IPrintServiceSPI> iterator = serviceLoader.iterator(); //迭代所用服务
while (iterator.hasNext()) {
iterator.next().print("test"); //使用服务
}
}
在 META-INF/services.文件夹下放入服务全限定名文件,eg:IPrintServiceSPI 全路径名com.kwxyzk.spi.IPrintServiceSPI
文件内放入服务提供商想要提供的服务
com.kwxyzk.spi.JavaPrintServiceSPI
com.kwxyzk.spi.JSONPrintServiceSPI
服务提供商加载工具ServiceLoader
ServiceLoader 使用迭代器设计模式和反射机制,使用迭代器遍历所有的供应商提供的申明服务名,使用反射通过实现的服务名反射为服务对象。
步骤
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
···
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
}
···
//懒加载服务到迭代器中
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
//hasnext 开始迭代时
public boolean hasNext() {
if (knownProviders.hasNext())
return true;
return lookupIterator.hasNext();
}
//判断时候还有实现服务
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
//PREFIX = "META-INF/services/";
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
//Iterator pending = null;
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
//从文件或者url中获取内容,放入pending 迭代器,内容为
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}
//使用服务 先迭代出services下文件中服务实现列表名,再映射为服务对象
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
//获取提供者中申明使用的实现服务
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);//提供者中申明使用的实现服务名反射成服务对象
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}