今天在看dubbo源码的时候,看到大量的SPI,对于SPI不是很明白,于是网上看资料和例子,有了这篇文章。
SPI(Service Provider Interface),是Java提供的一套用来被第三方实现或者扩展的API,可以用来启动框架扩展和替换组件。

使用场景
- 数据库驱动加载
- dubbo
- 日志门面模式实现不同日志
SPI 的使用
定义接口并实现接口
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | public interface Spi {
 
 
 
 void sayHello();
 }
 public class Cat implements Spi {
 
 @Override
 public void sayHello() {
 System.out.println("Hello World! This is a Cat");
 }
 }
 
 public class Dog implements Spi {
 
 @Override
 public void sayHello() {
 System.out.println("Hello World! This is a Dog");
 }
 }
 
 
 | 
src/main/resources/ 创建文件
在src/main/resources/ 目录下创建 /META-INF/services文件(关于services文件夹,如果使用Java自带的需要使用这个名字,如果自己实现可以自定义),并在文件夹中创建与接口同名的文件——com.example.spi.Spi:

测试
| 12
 3
 4
 5
 6
 7
 8
 
 | public static void main(String[] args) {ServiceLoader<Spi> services = ServiceLoader.load(Spi.class);
 Iterator<Spi> iterator = services.iterator();
 while (iterator.hasNext()) {
 iterator.next().sayHello();
 }
 }
 
 
 | 
输出:
| 12
 
 | Hello World! This is a CatHello World! This is a Dog
 
 | 
SPI 原理解析
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | public final class ServiceLoader<S> implements Iterable<S>{
 private final Class<S> service;
 
 private final String serviceName;
 
 private final ModuleLayer layer;
 
 
 private final ClassLoader loader;
 
 
 private final AccessControlContext acc;
 
 
 private Iterator<Provider<S>> lookupIterator1;
 private final List<S> instantiatedProviders = new ArrayList<>();
 
 
 private Iterator<Provider<S>> lookupIterator2;
 private final List<Provider<S>> loadedProviders = new ArrayList<>();
 private boolean loadedAllProviders;
 }
 
 | 
流程
- 用ServiceLoader.load方法ServiceLoader.load方法内先创建一个新的ServiceLoader,并实例化该类中的成员变量,包括: - 
- loader(ClassLoader类型,类加载器)
-   acc(AccessControlContext类型,访问控制器)
-   providers(LinkedHashMap<String,S>类型,用于缓存加载成功的类)
- lookupIterator(实现迭代器功能)
 
- 应用程序通过迭代器接口获取对象实例 
- 读取配置文件 
- 通过反射Class.forName()加载类对象,并初始化 
- 把实例化后的类缓存到providers对象中,然后返回实例对象 
源码