SPI机制,是一种将服务接口与服务实现分离以达到解耦、大大提升了程序可扩展性的机制。引入服务提供者就是引入了spi接口的实现者,通过本地的注册发现获取到具体的实现类,轻松可插拔,SPI实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载,为某个接口寻找服务实现的机制
java spi的具体约定为:当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader
1、可替换的插件机制
典型应用,jdbc的实现机制。通常各大厂商(如Mysql、Oracle)会根据一个统一的规范(java.sql.Driver)开发各自的驱动实现逻辑。客户端使用jdbc时不需要去改变代码,直接引入不同的spi接口服务即可。Mysql的则是com.mysql.jdbc.Drive,Oracle则是oracle.jdbc.driver.OracleDriver
2、框架的自定义扩展,比如spring的一些接口,阿里的dubbo也是通过spi来实现自定义扩展
用加减乘除来演示
1、定义服务接口
public interface OperationService {
Object operation(int a, int b);
}
2、定义具体实现类
public class MultiplyOperationService implements OperationService {
@Override
public Object operation(int a, int b) {
int result = a * b;
System.out.println("multply---> args[a = "+a+",b = "+b+"],result: a * b = "+result );
return result;
}
}
3、src/main/resources/下建立/META-INF/services 目录,新增一个以接口命名的文件
4、接口命名的文件填入如下内容
注:文件的内容可以有多行,每行都是该接口对应的具体实现类的全限定名
5、使用 ServiceLoader 来加载配置文件中指定的实现
public OperationService getService(String key) {
if (StringUtils.isBlank(key)) {
throw new RuntimeException("key is null");
}
key = key.toLowerCase();
OperationService operationService = serviceMap.get(key);
if (operationService == null) {
ServiceLoader<OperationService> services = ServiceLoader.load(OperationService.class);
Iterator<OperationService> iterator = services.iterator();
while (iterator.hasNext()) {
operationService = iterator.next();
String clzName = operationService.getClass().getSimpleName();
String type = StringUtils.substring(clzName, 0, StringUtils.lastIndexOf(clzName, OperationService.class.getSimpleName()));
if (StringUtils.isNotBlank(type)) {
type = type.toLowerCase();
}
if (type.equals(key)) {
serviceMap.put(key, operationService);
break;
}
}
operationService = serviceMap.get(key);
}
if(operationService == null){
throw new RuntimeException("operationService is not found with key :"+key);
}
return operationService;
}
6、测试
public static void main(String[] args) {
ServiceUtil.getInstance().initCache();
OperationService operationService = ServiceUtil.getInstance().getService("add");
operationService.operation(10,2);
operationService = ServiceUtil.getInstance().getService("divide");
operationService.operation(10,2);
operationService = ServiceUtil.getInstance().getService("minus");
operationService.operation(10,2);
operationService = ServiceUtil.getInstance().getService("multiply");
operationService.operation(10,2);
}
<dependency>
<groupId>com.demo.spi</groupId>
<artifactId>spi_operation_add</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.demo.spi</groupId>
<artifactId>spi_operation_minus</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.demo.spi</groupId>
<artifactId>spi_operation_divide</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.demo.spi</groupId>
<artifactId>spi_operation_multiply</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
7、运行结果
add---> args[a = 10,b = 2],result: a + b = 12
divide---> args[a = 10,b = 2],result: a / b = 5
minus---> args[a = 10,b = 2],result: a - b = 8
multply---> args[a = 10,b = 2],result: a * b = 20
https://github.com/lyb-geek/spi_demo
1、https://www.cnblogs.com/lovesqcc/p/5229353.html
2、https://blog.csdn.net/sigangjun/article/details/79071850