Java SPI机制与应用

kyaa111 5月前 ⋅ 128 阅读

SPI全称Service Provider Interface, 是Java提供的一套用来被第三方实现或者扩展的接口

实际上是"基于接口的编程+策略模式+配置文件"组合实现的动态加载机制, 更是设计模式的生动体现

它可以用来启用框架扩展和替换组件. SPI的作用就是为这些被扩展的API寻找服务实现

我们一般推荐模块之间基于接口编程, 模块之间不对实现类进行硬编码, 一旦代码里涉及具体的实现类, 就违反了可拔插的原则, 如果需要替换一种实现, 就需要修改代码

简单demo

public interface UploadFile {

    void upload(String file);
}


public class AliyunCDN implements UploadFile {
    @Override
    public void upload(String url) {
        System.out.println("upload to AliyunCDN");
    }
}

public class QiniuCDN implements UploadFile {
    @Override
    public void upload(String url) {
        System.out.println("upload to QiniuCDN");
    }
}

然后需要在resources目录下新建META-INF/services目录, 并且在这个目录下新建一个与上述接口的全限定名一致的文件, 在这个文件中写入接口的实现类的全限定名

org.test.AliyunCDN

org.test.QiniuCDN

启动类

public class Test {
    public static void main(String[] args) {
        ServiceLoader<UploadFile> uploadFiles = ServiceLoader.load(UploadFile.class);
        for (UploadFile u : uploadFiles) {
            u.upload("filePath");
        }
    }
}

项目结构

C:
├─pom.xml
└─src
   └─main
      ├─java
      │  └─org
      │      └─test
      │              AliyunCDN.java
      │              QiniuCDN.java
      │              Test.java
      │              UploadFile.java
      └─resources
          └─META-INF
              └─services
                      org.test.UploadFile

运行

upload to AliyunCDN
upload to QiniuCDN

使用场景

  • Spring

    Spring中大量使用了SPI, 比如: 对Servlet3.0规范对ServletContainerInitializer的实现(Tomcat加载SpringBoot项目的War包)/自动类型转换Type Conversion SPI(Converter SPI/Formatter SPI)等

  • SpringBoot

    SpringBoot中spring.factories和SpringFactoriesLoader

  • Hutool

    拼音工具-PinyinUtil 官方文档

    和其它门面模块类似,采用SPI方式识别所用的库。例如你想用Pinyin4j,只需引入jar,Hutool即可自动识别。

  • Dubbo

    Dubbo中也大量使用SPI的方式实现框架的扩展, 不过它对Java提供的原生SPI做了封装,允许用户扩展实现Filter接口

  • JDBC

    JDBC4.0以后不再需要Class.forName即可注册驱动