主题
介绍 Java
的 SPI
机制
什么是 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 SPI
、Formatter 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"); } }
|