转自:https://www.jianshu.com/p/46b42f7f593c
一、SPI是什么
SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件
整体机制图如下:
Java SPI实际上是基于接口的编程+策略模式+配置文件组合实现的动态加载机制
系统设计的各个抽象,往往有很多不同的实现方案,在面向的对象的设计里,一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制
Java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦
二、使用场景
概括地说,适用于调用者根据实际使用需要,启用、扩展、或者替换框架的实现策略
比较常见的例子:
- 数据库驱动加载接口实现类的加载:JDBC加载不同类型数据库的驱动
- 日志门面接口实现类加载:SLF4J加载不同提供商的日志实现类
- Spring:Spring中大量使用了SPI,比如:对Servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等
- Dubbo:Dubbo中也大量使用SPI的方式实现框架的扩展,不过它对Java提供的原生SPI做了封装,允许用户扩展实现Filter接口
三、使用介绍
要使用Java SPI,需要遵循如下约定:
1)、当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services
目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
2)、接口实现类所在的jar包放在主程序的classpath中
3)、主程序通过java.util.ServiceLoader
动态装载实现模块,它通过扫描META-INF/services
目录下的配置文件找到实现类的全限定名,把类加载到JVM
4)、SPI的实现类必须携带一个不带参数的构造方法
提供的驱动接口:
package com.hand.spi.demo.driver;
public interface IDriver {
void run();
}
驱动A实现:
package com.hand.spi.demo.driver.impl;
import com.hand.spi.demo.driver.IDriver;
public class DriverAImpl implements IDriver {
@Override
public void run() {
System.out.println("驱动A");
}
}
驱动B实现:
package com.hand.spi.demo.driver.impl;
import com.hand.spi.demo.driver.IDriver;
public class DriverBImpl implements IDriver {
@Override
public void run() {
System.out.println("驱动B");
}
}
实现主类:
package com.hand.spi.demo;
import com.hand.spi.demo.driver.IDriver;
import java.util.ServiceLoader;
public class SpiMain {
public static void main(String[] args) {
ServiceLoader<IDriver> drivers = ServiceLoader.load(IDriver.class);
for (IDriver d : drivers) {
d.run();
}
}
}
在resources下创建META-INF/services
目录, 然后创建以接口全限定名为文件名的文件, 文件内写入需要被发现的实现类的全限定名
执行结果:
驱动A
驱动B
四、源码分析
从上面代码中知,SPI的核心操作类就是java.util.ServiceLoader
这个类
ServiceLoader<IDriver> drivers = ServiceLoader.load(IDriver.class);
1、属性
public final class ServiceLoader<S>
implements Iterable<S>
{
//指定读取实现接口信息的文件位置
private static final String PREFIX = "META-INF/services/";
//指定要发现实现的接口
private final Class<S> service;
//类加载器(将实现类加载到JVM中)
private final ClassLoader loader;
//安全上下文
private final AccessControlContext acc;
//所有被发现的接口实现集合
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
//懒加载迭代器(ServiceLoader内部类)
private LazyIterator lookupIterator;
2、初始化
以下按调用顺序从上至下
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)
{
//构造器构造ServiceLoader
return new ServiceLoader<>(service, loader);
}
private ServiceLoader(Class<S> svc, ClassLoader cl) {
//判断接口是否存在,若不存在,抛出异常"Service interface cannot be null"
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);
}
初始化了service、loader、acc、lookupIterator
3、类定义
ServiceLoader类实现了Iterable接口, 这个类通过本类实例迭代器, 关联到属性
private LinkedHashMap<String,S> providers
中
public Iterator<S> iterator() {
return new Iterator<S>() {
//返回的迭代器,其实是providers属性的迭代器
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();
//迭代器返回的结果是通过LazyIterator迭代器返回的结果
return lookupIterator.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
ServiceLoader实现了Iterator接口, 方便用户通过serviceLoader遍历 实现的集合, 并隐藏内部具体的实现, 自己本身的迭代器是通过迭代维护实现的集合 providers 属性实现的, 在迭代providers时, 文件中发现的接口实现还没有被加载到JVM中, 在内部类LazyIterator中以懒加载的形式, 将接口实现类加载到JVM中, 并且实例化到providers中
4、迭代器
这是一个内部类, 通过迭代器间接调用LazyIterator的迭代方法, 在懒加载迭代器的迭代方法中, 通过反射对发现的实现类进行加载和实例化
private class LazyIterator
implements Iterator<S>
{
Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
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 {
//此处获取到MATE-INF/service下的文件,读取为多个资源文件路径Enumeration<URL>
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
}
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);
}
}
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);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}