1 Java SPI介绍
SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。提供了为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦。
1.1 使用场景
概括地说,适用于调用者根据实际使用需要启用、扩展、或者替换框架的实现策略。比较常见的例子:
- 数据库驱动加载接口实现类的加载:JDBC加载不同类型数据库的驱动
- 日志门面接口实现类加载:SLF4J加载不同提供商的日志实现类
- Spring:Spring中大量使用了SPI,比如:对servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等
- Dubbo:Dubbo中也大量使用SPI的方式实现框架的扩展,不过Dubbo自己实现了一套SPI的机制。
1.2 使用介绍
要使用Java SPI,需要遵循如下约定:
- 当服务提供者提供了接口的一种具体实现后,在jar包的
META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名 - 接口实现类所在的jar包放在主程序的classpath中
- 主程序通过
java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM - SPI的实现类必须携带一个不带参数的构造方法
1.3 简单使用
# 1 准备文件- META-INFO/services/- com.ankoye.People # 文件:与接口全限定名一致- com.ankoye.BlackPeople # 内容:实现接口的类- com.ankoye.WhitePeople# 2 测试ServiceLoader<People> peoples = ServiceLoader.load(People.class);for (People people : peoples) {// 两个}
2 Dubbo SPI
2.1 介绍
Dubbo SPI提供了比Java SPI更加强大的功能,主要有:
- 可以获取单个指定的实现类
- 添加了类似AOP的Wrapper功能
- 实现了类似DI的Adaptive功能
2.2 简单使用
# 1 准备文件- META-INFO/dubbo/- com.ankoye.People # 文件:与接口全限定名一致- black=com.ankoye.BlackPeople # 内容:实现接口的类- white=com.ankoye.WhitePeople# 2 测试ExtensionLoader<People> extensionLoader = ExtensionLoader.getExtensionLoader(People.class);People people = extensionLoader.getExtension("black");
3 应用
3.1 Wrapper
# 1 准备文件- META-INFO/dubbo/- com.ankoye.People- black=com.ankoye.BlackPeople- white=com.ankoye.WhitePeople- com.ankoye.PeopleWrapper# 2 Wrapper类,这个类需要实现接口,并且需要有个接口类型成员变量和构造public class PeopleWrapper implements People {private People people;public PeopleWrapper(People people) {this.people = people;}}# 3 测试ExtensionLoader<People> extensionLoader = ExtensionLoader.getExtensionLoader(People.class);People people = extensionLoader.getExtension("black"); // 获取到的是Wrapper对象
3.2 @SPI
@SPI("black") // black为默认的实现public interface People {}
3.3 @Extention
@Extension("black") // 指定前缀,不推荐public class BlackPeople implements People {}
3.4 @Adaptive
@Adaptive 与依赖注入有关,比如我们需要在People类中注入一个Address对象,则Address对象需要加@SPI且每个方法需要加@Adaptive注解。被注入的Address对象是一个代理对象。
@SPIpublic interface Address {@AdaptiveString getAddress(URL url);}public class BlackPeople implements People {private Address address;public void setAddress(Address address) {this.address = address;}}
4 源码解析

