apache shenyu的spi机制
Contents
什么是spi
SPI就是Service Provider Interface,是一种动态的服务发现机制。
可以基于运行时动态的加载接口的实现类。通过(接口编程 + 策略模式 + 配置文件) 实现解耦和扩展。

应用
- 数据库驱动
- 基于该接口的实现有
MySQL,PostgreSQL,SqlServer等。
- 基于该接口的实现有
- SLF4J日志框架
Dubbo的SPI扩展- dubbo
- apache shenyu
spring boot starter
jdbc的应用

日志的应用

apache shenyu的spi应用

dubbo的spi应用

实现方式
市面上常见的SPI的实现方式有:
JDK的SPI实现Dubbo的SPI实现、ShenYu的SPISpring的SPI实现
JDK的SPI实现流程为
- 在类路径的
META-INF/services目录创建一个以接口全限定名称命名的文件(本质是一个properties)文件,例如命名为java.sql.Driver - 该文件中可以指定具体的实现类,也就是每个实现类的全类型限定名为单独一行
- 例如
META-INF/services/java.sql.Driver -
# META-INF/services/java.sql.Driver文件内容 com.mysql.jdbc.Driver org.postgresql.Driver
- 例如
- 最后通过
java.util.ServiceLoader对该文件进行加载,实例化接口的对应实现类(这里隐含了一个约定,所有实现类必须提供无参构造函数,用来反射调用)
底层的实现涉及到类加载、双亲委派等内容,这里不展开。基于这种设计思路,很多主流框架实现了一套SPI扩展,例如Dubbo的SPI扩展模块,就是读取类路径
下META-INF/services/dubbo目录的文件内容进行类加载。ShenYu-SPI模块也是沿用类似的设计思路。
JDK SPI 的代码示例
- 接口定义
public interface Search {
public List<String> searchDoc(String keyword);
}
- 实现类
public class FileSearch implements Search{
@Override
public List<String> searchDoc(String keyword) {
System.out.println("文件搜索 "+keyword);
return null;
}
}
public class DatabaseSearch implements Search{
@Override
public List<String> searchDoc(String keyword) {
System.out.println("数据搜索 "+keyword);
return null;
}
}
- resources 接下来可以在resources下新建META-INF/services/目录,然后新建接口全限定名的文件:
com.xxx.Search,里面加上我们需要用到的实现类
com.xxx.FileSearch
- 测试方法
public class TestCase {
public static void main(String[] args) {
ServiceLoader<Search> s = ServiceLoader.load(Search.class);
Iterator<Search> iterator = s.iterator();
while (iterator.hasNext()) {
Search search = iterator.next();
search.searchDoc("hello world");
}
}
}
- 总结
可以看到输出结果:文件搜索 hello world如果在com.xxx.Search文件里写上两个实现类,那最后的输出结果就是两行了。
这就是因为ServiceLoader.load(Search.class)在加载某接口时,会去META-INF/services下找接口的全限定名文件,
再根据里面的内容加载相应的实现类。这就是spi的思想,接口的实现由provider实现,provider只用在提交的jar包里的META-INF/services下根据平台定义的接口新建文件,并添加进相应的实现类内容就好。
ShenYu SPI
源码分析
ShenYu-SPI模块十分精炼,代码结构如下
- shenyu-spi[module]
- org.apache.shenyu.spi[package]
-- ExtensionFactory
-- ExtensionLoader
-- Join
-- SPI
-- SpiExtensionFactory
这些类的功能如下
ExtensionFactory:SPI加载工厂,本身也是一个SPI,用于基于SPI机制加载ExtensionLoader实例同时基于ExtensionLoader实例获取默认标记SPI标识接口的实现SpiExtensionFactory:ExtensionFactory的一个实现类SPI: 标识注解,用于标识SPI,用于接口上Join: 标识注解,用于实现类上,用来给SPI的实现进行排序。ExtensionLoader:SPI加载器,类比java.util.ServiceLoader,用于加载SPI中接口的实现类