SPI的全名是Service Provider Interface,这个概念最初是针对于厂商或者插件的。java项目中,不同模块之间通过接口进行调用,接口会有很多的实现类,具体使用哪个,很多时候调用方并不想写死,写死也就是硬编码,这不符合可插拔的原则。就像IOC那样,spi机制将装配的控制权转移到程序之外,这种机制对于模块化编程非常重要。本文主要介绍下如何使用spi,以及SPI的实现原理
SPI的使用demo
俄罗斯进攻乌克兰作成接口,实现类有导弹进攻,坦克进攻等,绍伊古将这些进攻实现写到一个小本子上,他在什么时间,什么地点,采取什么样的进攻方式,不是固定不变的,是有选择性的,也许他会采取一种进攻方式,也可能把所有的进攻方式都用上,真正实现了打法的灵活多变。
进攻接口:
public interface Attack {
void doAttack();
}
实现类:
public class MissileAttack implements Attack{
@Override
public void doAttack(){
System.out.println("俄罗斯的导弹攻击");
}
}
public class TankAttack implements Attack {
public void doAttack(){
System.out.println("俄罗斯的坦克攻击");
}
}
我们需要在resources目录下新建META-INF/services目录,并且在这个目录下新建一个与上述接口的全限定名一致的文件,在这个文件中写入接口的实现类的全限定名:
绍伊古的小本子
通过serviceLoader加载实现类并调用:
public static void main(String[] args){
ServiceLoader<Attack> serviceLoader = ServiceLoader.load(Attack.class);
for(Attack attack:serviceLoader){
attack.doAttack();
}
}
在实际使用中,我们可以使用接口文件中配置的第一个实现类作为实际的执行者。也可以结合apollo配置,例如我们可以将实现类的序号配置到apollo中,来选择实际的执行者。
SPI的实现原理:
主要实现类为ServiceLoader,它实现了Iterable接口。
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();
}
在创建ServiceLoader的时候,内部创建了LazyIterator迭代器。LazyIterator迭代器的next方法会创建出一个个实现类。下面我们重点看下LazyIterator迭代器内的hasNextService()方法和nextService()方法:
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
}