介绍 Java 的 SPI 机制

主题

介绍 JavaSPI 机制

什么是 SPI

Service Provider Interface,简称 SPI。实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。

graph LR
	subgraph Java SPI 机制
        I[调用方];
        A{接口};
        subgraph 服务提供方
            C1[实现者1];
            C2[实现者2];
        end
        I-->|调用|A;
        A-->|本地服务发现 & 服务发现|C1;
	end

使用场景

数据库驱动加载接口实现类的加载

JDBC 加载不同类型数据库的驱动

日志门面接口实现类加载

SLF4J 加载不同提供商的日志实现类

Spring

Spring 中大量使用了 SPI,比如:对 servlet3.0 规范对 ServletContainerInitializer 的实现、自动类型转换 Type Conversion SPI(Converter SPIFormatter SPI)等

Dubbo

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

样例

Joy,定义接口

public interface Joy {
void speak();
}

ServiceLoadDemo,加载多个实现

public class ServiceLoadDemo {
public static <T> List<T> loadFromServiceLoader(Class<T> clazz) {
List<T> list = new ArrayList<>();
ServiceLoader<T> loader = ServiceLoader.load(clazz);
Iterator<T> iterator = loader.iterator();
while (iterator.hasNext()) {
list.add(iterator.next());
}
return list;
}
}

LoadObjectBySPI,实际调用者

public class LoadObjectBySPI {
public static void main(String[] args) {
List<Joy> list = ServiceLoadDemo.loadFromServiceLoader(Joy.class);
for (Joy joy : list) {
joy.speak();
}
}
}

demo.Joy.properties,配置文件,必须放 META-INF/services下,以接口全名命名。实现类写里面,一行一个。

demo.impl.BirdJoy
demo.impl.CatJoy
demo.impl.DogJoy
demo.impl.DuckJoy
demo.impl.FrogJoy
demo.impl.HorseJoy

实现类:

public class BirdJoy implements Joy {
@Override
public void speak() {
System.out.println(this.getClass().getSimpleName() + ":tweet");
}
}
public class CatJoy implements Joy {
@Override
public void speak() {
System.out.println(this.getClass().getSimpleName() + ":meow");
}
}
public class DogJoy implements Joy {
@Override
public void speak() {
System.out.println(this.getClass().getSimpleName() + ":woof");
}
}
public class DuckJoy implements Joy {
@Override
public void speak() {
System.out.println(this.getClass().getSimpleName() + ":quack");
}
}
public class FrogJoy implements Joy {
@Override
public void speak() {
System.out.println(this.getClass().getSimpleName() + ":croak");
}
}
public class HorseJoy implements Joy {
@Override
public void speak() {
System.out.println(this.getClass().getSimpleName() + ":neigh");
}
}