SPI(Service Provider Interface) 是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。
java中实现spi的主要类为 ServiceLoader<S>,一个简单的服务提供商加载工具。 一个服务是一组众所周知的接口(通常是抽象的)类。服务提供商是服务的具体实现。提供者中的类通常实现接口并对服务本身定义的类进行子类化。服务提供商可以以Java扩展的形式安装在Java平台的实现中,也就是将jar文件放置到任何通常的扩展目录中。提供商也可以通过将它们添加到应用程序的类路径或某些其他平台特定的方式来提供。
1. java spi的实现原理
java 中通过实现接口,通过基于接口的编程模式,将服务规范与服务提供相分离,实现不同的厂商提供服务。比如在支付平台中,需要集成多个银行及第三方支付渠道,这时就可以定义统一的支付接口,根据不同的支付渠道,提供不同的支付服务实现。
要使用Java SPI,需要遵循如下约定:
(1)当服务提供者提供了接口的具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;
(2)引入接口实现类所在的jar包;
(3)主程序通过java.util.ServiceLoder<S>动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;
(4)SPI的实现类必须携带一个不带参数的构造方法;
2. 代码实现
代码实现包括接口定义,接口实现,接口配置以及调用示例。
2.1 接口定义
package com.platform.service;
// 数据操作接口
public interface IDataOperator {
void insertData(String str);
}
定义一个数据操作接口。
2.2 Mysql实现
package com.platform.service;
public class MysqlDataOperator implements IDataOperator{
@Override
public void insertData(String str) {
System.out.println("Mysql insert data:" + str);
}
}
2.3 Oracle实现
package com.platform.service;
public class OracleDataOperator implements IDataOperator {
@Override
public void insertData(String str) {
System.out.println("Oracle insert data:" + str);
}
}
2.4 服务配置
在resources目录下新建META-INF/services目录,并且在这个目录下新建一个与上述接口的全限定名一致的文件"com.platform.service.IDataOperator",在这个文件中写入接口的实现类的全限定名。
com.platform.service.MysqlDataOperator
com.platform.service.OracleDataOperator
2.5 服务调用
package com.platform.server;
import com.platform.service.IDataOperator;
import com.platform.service.MysqlDataOperator;
import com.platform.service.OracleDataOperator;
import java.util.Iterator;
import java.util.ServiceLoader;
public class AppServer {
public static void main(String[] args) {
ServiceLoader<IDataOperator> serviceLoader = ServiceLoader.load(IDataOperator.class); //加载服务
Iterator<IDataOperator> iterator = serviceLoader.iterator(); //迭代所用服务
while (iterator.hasNext()) {
IDataOperator dataOperator = iterator.next();
if(dataOperator instanceof MysqlDataOperator)
{
System.out.println("调用mysql服务");
// 调用mysql服务
dataOperator.insertData("test data");
}
else if (dataOperator instanceof OracleDataOperator)
{
System.out.println("调用Oracle服务");
// 调用Oracle服务
dataOperator.insertData("test data");
}
}
}
}
执行测试,输出如下:
调用mysql服务
Mysql insert data:test data
调用Oracle服务
Oracle insert data:test data
以上输出说明加载了接口的实现服务,并且调用了服务的实现方法。
3. SPI技术的优势
总结下spi能带来的好处:
- 不需要改动源码就可以实现扩展,解耦。
- 实现扩展对原来的代码几乎没有侵入性。
- 只需要添加配置就可以实现扩展,符合开闭原则。
Demo源码下载地址: