什么是SPI
SPI全称是Service Provider Interface,是Java提供的一套用来被第三方实现或扩展的API,可以用来启用框架的扩展或组件的替换。
SPI的应用
SPI有很多的使用场景,比如:
- JDBC中的使用,各种数据库的驱动实现
- SLF4J中的使用,加载不同的日志实现
- Spring中的使用,对servlet的ServletContainerInitializer的实现
……
SPI的实战
- 创建一个接口
public interface DataBaseDriver { void driver(); }
- 实现该接口
public class MyDataBaseDriver implements DataBaseDriver { @Override public void driver() { System.out.println("这是MyDataBase的驱动"); } }
- 在实现接口的项目的resources目录下创建META-INF/services目录,并创建以接口全类名命名的文件,里面写上接口实现的全类名
文件名:com.demo.DataBaseDriver
文件内容:com.demo.MyDataBaseDriver - 通过
ServiceLoader
进行使用public class DataBaseDriverTest { public static void main(String[] args) { ServiceLoader<DataBaseDriver> dataBaseDrivers = ServiceLoader.load(DataBaseDriver.class); for (DataBaseDriver dataBaseDriver : dataBaseDrivers) { dataBaseDriver.driver(); } } }
SPI的原理
- 通过
ServiceLoader.load(Class<S> service)
实例化一个ServiceLoader
对象 - 读取文件内容
- 通过反射得到对象
关键源码如下:
private static final String PREFIX = "META-INF/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
}