SPI(Service provider interface)是旨在由第三方实现或扩展的API。它可以用于启用框架扩展和可替换组件。
服务是一组众所周知的接口或(通常是抽象的)类。服务提供者是服务的特定实现。提供程序中的类通常实现接口并对服务本身中定义的类进行子类化。服务提供程序可以以扩展的形式安装在Java平台的实现中,即,jar文件放置在任何通常的扩展目录中。提供者也可以通过将它们添加到应用程序的类路径或通过一些其他平台特定的手段。
在java中通过java.util.ServiceLoader 来实现。
先上代码:
Provider 可以为接口或者抽象类
package com.xwolf.spi.service; /** * Animal Provider * @author xwolf * */ public interface Animal { void eat(); void drink(); }
具体实现:
package com.xwolf.spi.service.impl; import com.xwolf.spi.service.Animal; public class Dog implements Animal { @Override public void eat() { System.out.println("Dog eat...."); } @Override public void drink() { System.out.println("Dog drink...."); } }
package com.xwolf.spi.service.impl; import com.xwolf.spi.service.Animal; public class Cat implements Animal { @Override public void eat() { System.out.println("Cat eat...."); } @Override public void drink() { System.out.println("Cat drink..."); } }
将接口和具体实现放在文件META-INF/services中,文件名为全类名的Provider,内容为具体实现类的全类名。
测试方法:
package com.xwolf.spi.service; import java.util.ServiceLoader; public class ServiceLoaderTest { public static void main(String[] args) { //接口测试 ServiceLoader<Animal> loader=ServiceLoader.load(Animal.class); for(Animal animal:loader){ animal.eat(); } } }
项目目录结构:
查看ServiceLoader部分源代码:
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 }
比较核心的代码就上边了,总之就是遍历的时候读取META-INF/services下所有为CLASS(本例子中的Animal)的全类名的文件内容,加载类。