一、什么是SPI机制
SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。
这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。
JDBC的设计,jdk提供了一个接口java.sql.Driver
,通常各大厂商(如Mysql、Oracle)会根据一个统一的规范(java.sql.Driver)开发各自的驱动实现逻辑。客户端使用jdbc时不需要去改变代码,直接引入不同的spi接口服务即可。
二、SPI机制的主要目的
- 为了解耦,将接口和具体实现分离开来;
- 提高框架的扩展性。以前写程序的时候,接口和实现都写在一起,调用方在使用的时候依赖接口来进行调用,无权选择使用具体的实现类。
三、SPI机制案例
- JDBC驱动加载案例:利用Java的SPI机制,我们可以根据不同的数据库厂商来引入不同的JDBC驱动包;
- SpringBoot的SPI机制:我们可以在spring.factories中加上我们自定义的自动配置类,事件监听器或初始化器等;
- Dubbo的SPI机制:Dubbo更是把SPI机制应用的淋漓尽致,Dubbo基本上自身的每个功能点都提供了扩展点,比如提供了集群扩展,路由扩展和负载均衡扩展等差不多接近30个扩展点。如果Dubbo的某个内置实现不符合我们的需求,那么我们只要利用其SPI机制将我们的实现替换掉Dubbo的实现即可。
例子:
接口
public interface SPIService {
void execute();
}
实现类1
public class SpiImpl1 implements SPIService{
public void execute() {
System.out.println("SpiImpl1.execute()");
}
}
实现类2
public class SpiImpl2 implements SPIService{
public void execute() {
System.out.println("SpiImpl2.execute()");
}
}
最后呢,要在ClassPath路径下配置添加一个文件。文件名字是接口的全限定类名,内容是实现类的全限定类名,多个实现类用换行符分隔。
com.example.test.demo.spi.SpiImpl1
com.example.test.demo.spi.SpiImpl2
测试:
public static void main(String[] args) {
// Service.providers方法拿到实现类的实例
Iterator<SPIService> providers = Service.providers(SPIService.class);
while(providers.hasNext()){
SPIService ser=providers.next();
ser.execute();
}
System.out.println("==============================================");
// 通过ServiceLoader.load方法拿到实现类的实例
// ServiceLoader是实现SPI一个重要的类。是Mark Reinhold在java1.6引入的类,为了解决接口与实现分离的场景。
ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class);
Iterator<SPIService> iterator = load.iterator();
while(iterator.hasNext()){
SPIService next = iterator.next();
next.execute();
}
}
结果:
SpiImpl1.execute()
SpiImpl2.execute()
==============================================
SpiImpl1.execute()
SpiImpl2.execute()