SPI(Service Provider Interface),是一种模块间相互引用的机制,可以用来启用框架和替换组件,一般的流程是服务的提供者在classpath指定配置实现类的全名,由调用方读取和加载使用,调用方无需修改代码,通常以jar包的形式引入需要使用的实现,Dubbo,Soul等项目使用了SPI机制,但给使用者提供了更丰富便捷的选择,可以由用户安优先级,名称等方式选择使用那个实现。
SPI 标准流程:
JDK内置的SPI 机制 ,约定好的classpath目录下META_INFO/services/ 创建一个以服务接口命名的文件,该文件中内容为该接口的实现类(完全名称即包含package名称)通过java.util.ServiceLoader加载指定接口
举个栗子
我们定义一个LearnSpi interface
package com.cuicui.bootcamp
public interface LearnSpi {
void readBook();
}
我们一个school的学习工程打包为school.jar 里面实现类:
package com.cuicui.bootcamp
import com.cuicui.bootcamp.LearnSpi
public class SchoolLearnSpi implements LearnSpi {
public void readBook(){
System.out.println(“School readBook”);
}
}
需要在school工程的classpath目录下META_INFO/services/ 中新建一个名为
com.cuicui.bootcamp. LearnSpi的文件
文件的内容为
com.cuicui.bootcamp.SchoolLearnSpi
JDK默认的SPI机制在使用的时候我们没有办法获取到我们想要的特定的实现,只能通过for循环一个一个遍历匹配,也没有命名,实际很难使用。
Soul 的spi机制,是由Soul-spi这个项目负责实现,并借鉴Dubbo SPI的实现
ExtensionLoader 提供了比JDK ServiceLoader更为强大的功能。
我们这里重点分析ExtensionLoader
类成员有:
// soul spi的扩展目录
private static final String SOUL_DIRECTORY = "META-INF/soul/";
// 不同扩展接口对应的ExtensionLoader的缓存类,静态成员
private static final Map<Class<?>, ExtensionLoader<?>> LOADERS = new ConcurrentHashMap<>();
// 扩展接口类
private final Class clazz;
// 自定的Holder 类型,cachedClasses 用于存储不同扩展对应的实现类们
private