SPI
SPI全称 Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。
- API (Application Programming Interface)在大多数情况下,都是实现方制定接口并完成对接口的实现,调用方仅仅依赖接口调用,且无权选择不同实现。 从使用人员上来说,API 直接被应用开发人员使用。
- SPI (Service Provider Interface)是调用方来制定接口规范,提供给外部来实现,调用方在调用时则选择自己需要的外部实现。 从使用人员上来说,SPI 被框架扩展人员使用。
SPI 示例
定义接口
package demo.java.util.ServiceLoader;
public interface Protocol {
String getName();
}
两个不同的实现类
package demo.java.util.ServiceLoader;
public class DubboProtocolImpl implements Protocol {
@Override
public String getName() {
return "dubbo";
}
}
package demo.java.util.ServiceLoader;
public class HttpProtocolImpl implements Protocol {
@Override
public String getName() {
return "http";
}
}
resources中配置 META-INF/services/demo.java.util.ServiceLoader.Protocol, 路径为 “META-INF/services/” + 接口全限定名, 内容就是该接口的两个实现类的全限定名, 一行一个
demo.java.util.ServiceLoader.DubboProtocolImpl
demo.java.util.ServiceLoader.HttpProtocolImpl
Demo演示
public class Demo {
public static void main(String[] args) {
ServiceLoader<Protocol> serviceLoader = ServiceLoader.load(Protocol.class);
for (Protocol protocol : serviceLoader) {
System.out.println(protocol.getClass());
System.out.println(protocol.getName());
}
}
}
结果
class demo.java.util.ServiceLoader.DubboProtocolImpl
dubbo
class demo.java.util.ServiceLoader.HttpProtocolImpl
http
真实案例
JDK定义了数据库操作规范 java.sql.Driver, 各厂家提供驱动包, 如mysql.jar和oracle.jar, 在 mysql-connector-java-8.0.20.jar 中有如下结构
其内容只有一行 com.mysql.cj.jdbc.Driver
, 即mysql提供的Driver接口的实现类, 在 DriverManager 类中有静态初始化代码如下
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
// ...
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
// 实例化
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
// ...
}
原理
编译前
public static void main(String[] args) {
ServiceLoader<Protocol> serviceLoader = ServiceLoader.load(Protocol.class);
for (Protocol protocol : serviceLoader) {
System.out.println(protocol.getClass());
System.out.println(protocol.getName());
}
}
编译后, 本质上还是获取到Iterator, 然后调用hasNext()和next()方法
public static void main(String[] args) {
ServiceLoader<Protocol> serviceLoader = ServiceLoader.load(Protocol.class);
Iterator var2 = serviceLoader.iterator();
while(var2.hasNext()) {
Protocol protocol = (Protocol) var2.next();
System.out.println(protocol.getClass());
System.out.println(protocol.getName());
}
}
ServiceLoader 关键源码
public final class ServiceLoader<S> implements Iterable<S> {
// 扫描目录前缀, 官方定死了
private static final String PREFIX = "META-INF/services/";
// 被加载的类或接口
private final Class<S> service;
// 用于定位、加载和实例化实现方实现的类的类加载器
private final ClassLoader loader;
// 上下文对象(权限控制)
private final AccessControlContext acc;
// 按照实例化的顺序缓存已经实例化的实现类
private LinkedHashMap<String, S> providers = new LinkedHashMap<>();
// 懒查找迭代器
private java.util.ServiceLoader.LazyIterator lookupIterator;
// 私有内部类,提供对所有的service的类的加载与实例化能力
private class LazyIterator implements Iterator<S> {
Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
String nextName = null;
//...
private boolean hasNextService() {
if (nextName != null) {
return true;
}
// 这个if只有第一次调用该方法时会走
if (configs == null) {
// 获取指定文件下的所有实现类配置
// 会从各类加载器路径下查找所有的该路径文件, 包括有如下
// 引导类加载器的 jre/lib/*.jar
// 扩展类加载器的 jre/lib/ext/.*.jar
// 应用类加载器的 classpath(*/target/classes/*) 和 其他引入的所有 jar
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);
}
}
// 最终应该是在 classpath 下找到了文件, 然后将其每一行解析成一个List, 最终pending=list.iterator()
// 这个while只有第一次调用该方法时会走
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
}
// 迭代器的 hasNext 方法, 调用 hasNextService
public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
// 迭代器的 next 方法, 调用 nextService
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
}
}