什么是SPI
SPI
全名是Service Provider Interface
,通过接口寻找其具体的实现类,从而避免了使用方与提供方之间的强耦合,实现了动态可拔插的特性。
比如JDBC
的数据库驱动模块,在JDBC以前的版本中,需要直接在代码中采用Class.forName(驱动实现类全名)
的方式进行驱动初始化,导致想要更换数据库驱动模块就需要修改代码,具有很强的耦合度。
Class.forName("com.mysql.jdbc.Driver");
// 下面是mysql的驱动实现类
package com.mysql.cj.jdbc;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
在后续版本中,JDBC
采用了SPI
机制,数据库驱动只需要在META-INF/services
下面新建一个与驱动接口全路径名相同的文件,里面写上其驱动实现类的全路径名,这样JDBC
就可以通过Java提供的ServiceLoad
接口动态的去加载。
SPI简单实战
- 首先定义一个接口
package com.dubbo.service;
public class AudiService implements ICarService {
@Override
public void drive() {
System.out.println("audi drive speed 100KM/H");
}
}
- 创建两个该接口的实现类
package com.dubbo.service;
public class AudiService implements ICarService {
@Override
public void drive() {
System.out.println("audi drive speed 100KM/H");
}
}
package com.dubbo.service;
public class BMWService implements ICarService {
@Override
public void drive() {
System.out.println("bmw drive speed 200KM/H");
}
}
- 在
resources/META-INF/service/接口全路径
目录下创建文件
// 我的文件路径:META-INF\services\com.dubbo.service.ICarService
com.dubbo.service.AudiService
com.dubbo.service.BMWService
- 创建测试类
package com.dubbo.service;
import java.util.ServiceLoader;
public class Test {
public static void main(String[] args) {
ServiceLoader<ICarService> services = ServiceLoader.load(ICarService.class);
for (ICarService service : services) {
service.drive();
}
}
}
- 执行main方法,输出如下:
audi drive speed 100KM/H
bmw drive speed 200KM/H
SPI原理
ServiceLoader这个类是SPI机制实现的关键,下面是该类的源码:
package java.util;
import java.io.IOException;
import java.net.URL;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
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;
// 用于存储服务提供者的Map
private LinkedHashMap<String, S> providers = new LinkedHashMap<>();
// 懒迭代器
private LazyIterator lookupIterator;
// 重新加载实现类,清空providers缓存,并生成新的懒迭代器
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(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();
}
// 懒迭代器定义
private class LazyIterator implements Iterator<S> {
Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
// META-INF下ICarService文件的迭代器
Iterator<String> pending = null;
// 实现类全名
String nextName = null;
private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
// 判断是否有下一个元素
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;
}
// 读取META-INF下ICarService文件中的实现类全名列表的迭代器
pending = parse(service, configs.nextElement());
}
// 获取META-INF下ICarService文件中的下一个实现类全名,并赋予nextName
nextName = pending.next();
return true;
}
// 获取下一个实现类
private S nextService() {
// 判断是否存在下一个实现类
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
// 获取nextName中对应的实现类的类对象
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 {
// 创建nextName中对应的实现类的实例
S p = service.cast(c.newInstance());
// 放入到providers缓存中
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service, "Provider " + cn + " could not be instantiated", x);
}
throw new Error(); // This cannot happen
}
// 实现Iterator的方法,判断是否还有下一个元素
@Override
public boolean hasNext() {
if (acc == null) {
// 调用hasNextService()方法,判断是否还存在下一个服务实例
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() {
return hasNextService();
}
};
return AccessController.doPrivileged(action, acc);
}
}
@Override
public S next() {
if (acc == null) {
// 调用nextService()获取下一个服务实现类对象
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() {
return nextService();
}
};
return AccessController.doPrivileged(action, acc);
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
public Iterator<S> iterator() {
return new Iterator<S>() {
Iterator<Map.Entry<String, S>> knownProviders = providers.entrySet().iterator();
public boolean hasNext() {
if (knownProviders.hasNext())
return true;
return lookupIterator.hasNext();
}
public S next() {
if (knownProviders.hasNext())
return knownProviders.next().getValue();
return lookupIterator.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
缺陷
- 不能按需加载。遍历所有实现类,并全部载入、实例化,造成了资源浪费、性能损耗。
- 获取类的方式不够灵活。只能通过Iterator迭代器进行遍历获取,而不能通过标识获取指定的实现类。
- 线程不安全。在多线程环境下使用ServiceLoader可能会出现问题。