SPI全称Service Provider Interface, 是Java提供的一套用来被第三方实现或者扩展的接口
实际上是"基于接口的编程+策略模式+配置文件"组合实现的动态加载机制, 更是设计模式的生动体现
它可以用来启用框架扩展和替换组件. SPI的作用就是为这些被扩展的API寻找服务实现
我们一般推荐模块之间基于接口编程, 模块之间不对实现类进行硬编码, 一旦代码里涉及具体的实现类, 就违反了可拔插的原则, 如果需要替换一种实现, 就需要修改代码
简单demo
代码语言:txt复制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目录, 并且在这个目录下新建一个与上述接口的全限定名一致的文件, 在这个文件中写入接口的实现类的全限定名
代码语言:txt复制org.test.AliyunCDN
org.test.QiniuCDN
启动类
代码语言:txt复制public class Test {
public static void main(String[] args) {
ServiceLoader<UploadFile> uploadFiles = ServiceLoader.load(UploadFile.class);
for (UploadFile u : uploadFiles) {
u.upload("filePath");
}
}
}
项目结构
代码语言:txt复制C:
├─pom.xml
└─src
└─main
├─java
│ └─org
│ └─test
│ AliyunCDN.java
│ QiniuCDN.java
│ Test.java
│ UploadFile.java
└─resources
└─META-INF
└─services
org.test.UploadFile
运行
代码语言:txt复制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即可注册驱动