1 概述
SPI(Service Provider Interface)是指一种服务发现机制,为某个特定的接口寻找服务的实现。在大规模的软件开发中经常采用这样的机制,实现模块之间基于接口编程,隐藏其实现细节,不同的服务提供商进行扩展实现,最终实现无需修改代码即可调用,完全无代码入侵,如在JDBC 4.0之后支持SPI的方式加载驱动程序。
2 使用场景
如common-logging 中只是提供了日志操作的相关接口,而具体实现交由其他服务提供者自己去实现,再如JDBC提供了抽象的驱动操作接口,具体实现交由其他服务提供商自己去实现,4.0之前需要手动代码中配置驱动类,但在4.0之后支持SPI的方式加载驱动程序。
3 如何使用
下面我们通过一个简单的案例代码来演示SPI的具体使用,如我们提供一个抽象的Protocol协议接口,但具体实现交由子系统自己实现
3.1 外部包中Protocol协议接口
public interface Protocol {
String defaultName();
}
3.2 外部包中提供ProtocolFactory
用来打印其他子系统接口实现的具体协议名称,JDK中提供通过ServiceLoader来加载接口的具体实现类
public class ProtocolFactory {
public static void outProtocolName() {
ServiceLoader<Protocol> protocols = ServiceLoader.load(Protocol.class);
for (Protocol protocol : protocols) {
System.out.println("协议名称:" + protocol.defaultName());
}
}
}
3.3 子系统中提供具体接口实现
子系统中依赖包含Protocol接口的外部包,然后分别提供HttpProtocol和RpcProtocol具体实现类
public class HttpProtocol implements Protocol {
@Override
public String defaultName() {
return "HttpProtocol";
}
}
public class RpcProtocol implements Protocol {
@Override
public String defaultName() {
return "RpcProtocol";
}
}
3.4 配置接口实现类
在子系统模块中的resources目录下新建META-INF\services目录,然后在services目录下新建com.os.china.spi.Protocol文件,文件中添加具体实现类的全路径名称
com.os.china.spi.HttpProtocol
com.os.china.spi.RpcProtocol
3.5 测试
public class MainBootstrap {
public static void main(String[] args) {
ProtocolFactory.outProtocolName();
}
}
执行上面Main函数可以看到输出
协议名称:HttpProtocol
协议名称:RpcProtocol
3.6 AutoService
通过以上简单案例来演示SPI的具体使用方式,通过接口与实现分离的方式可以达到灵活扩展的目的,不足的是需要手动创建Protocol接口服务的配置文件,这里推荐可以通过Google提供的auto-service包中的@AutoService注解类自动创建该配置文件,十分便捷
3.6.1 引入AutoService包
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc4</version>
</dependency>
3.6.2 实现类标注@AutoService(Protocol.class)注解
分别在HttpProtocol与RpcProtocol实现类上标注@AutoService(Protocol.class)注解
@AutoService(Protocol.class)
public class HttpProtocol implements Protocol {
@Override
public String defaultName() {
return "HttpProtocol";
}
}
@AutoService(Protocol.class)
public class RpcProtocol implements Protocol {
@Override
public String defaultName() {
return "RpcProtocol";
}
}
然后启动测试类发现AutoService会自动帮我们创建services配置文件
4 总结
通过以上简单案例来演示SPI的具体使用方式,通过接口与实现分离的方式可以达到灵活扩展的目的,并且介绍了Google提供的AutoService来辅助实现SPI更加便捷。