1 Java SPI介绍

SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。提供了为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦
spi.webp

1.1 使用场景

概括地说,适用于调用者根据实际使用需要启用、扩展、或者替换框架的实现策略。比较常见的例子:

  • 数据库驱动加载接口实现类的加载:JDBC加载不同类型数据库的驱动
  • 日志门面接口实现类加载:SLF4J加载不同提供商的日志实现类
  • Spring:Spring中大量使用了SPI,比如:对servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等
  • Dubbo:Dubbo中也大量使用SPI的方式实现框架的扩展,不过Dubbo自己实现了一套SPI的机制。

1.2 使用介绍

要使用Java SPI,需要遵循如下约定:

  1. 当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
  2. 接口实现类所在的jar包放在主程序的classpath中
  3. 主程序通过java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM
  4. SPI的实现类必须携带一个不带参数的构造方法

1.3 简单使用

  1. # 1 准备文件
  2. - META-INFO/services/
  3. - com.ankoye.People # 文件:与接口全限定名一致
  4. - com.ankoye.BlackPeople # 内容:实现接口的类
  5. - com.ankoye.WhitePeople
  6. # 2 测试
  7. ServiceLoader<People> peoples = ServiceLoader.load(People.class);
  8. for (People people : peoples) {
  9. // 两个
  10. }

2 Dubbo SPI

2.1 介绍

Dubbo SPI提供了比Java SPI更加强大的功能,主要有:

  1. 可以获取单个指定的实现类
  2. 添加了类似AOP的Wrapper功能
  3. 实现了类似DI的Adaptive功能

2.2 简单使用

  1. # 1 准备文件
  2. - META-INFO/dubbo/
  3. - com.ankoye.People # 文件:与接口全限定名一致
  4. - black=com.ankoye.BlackPeople # 内容:实现接口的类
  5. - white=com.ankoye.WhitePeople
  6. # 2 测试
  7. ExtensionLoader<People> extensionLoader = ExtensionLoader.getExtensionLoader(People.class);
  8. People people = extensionLoader.getExtension("black");

3 应用

3.1 Wrapper

  1. # 1 准备文件
  2. - META-INFO/dubbo/
  3. - com.ankoye.People
  4. - black=com.ankoye.BlackPeople
  5. - white=com.ankoye.WhitePeople
  6. - com.ankoye.PeopleWrapper
  7. # 2 Wrapper类,这个类需要实现接口,并且需要有个接口类型成员变量和构造
  8. public class PeopleWrapper implements People {
  9. private People people;
  10. public PeopleWrapper(People people) {
  11. this.people = people;
  12. }
  13. }
  14. # 3 测试
  15. ExtensionLoader<People> extensionLoader = ExtensionLoader.getExtensionLoader(People.class);
  16. People people = extensionLoader.getExtension("black"); // 获取到的是Wrapper对象

3.2 @SPI

  1. @SPI("black") // black为默认的实现
  2. public interface People {
  3. }

3.3 @Extention

  1. @Extension("black") // 指定前缀,不推荐
  2. public class BlackPeople implements People {
  3. }

3.4 @Adaptive

@Adaptive 与依赖注入有关,比如我们需要在People类中注入一个Address对象,则Address对象需要加@SPI且每个方法需要加@Adaptive注解。被注入的Address对象是一个代理对象。

  1. @SPI
  2. public interface Address {
  3. @Adaptive
  4. String getAddress(URL url);
  5. }
  6. public class BlackPeople implements People {
  7. private Address address;
  8. public void setAddress(Address address) {
  9. this.address = address;
  10. }
  11. }

4 源码解析

SPI源码.png