Java SPI 居然这么多知名框架在用
Java SPI全称Java Service Provider Interface。是 Java 提供的一套用来被第三方实现或者扩展的 API,它可以用来启用框架扩展和替换组件。你可以理解为接口的自动注册发现,它的应用其实非常广泛,微服务通讯组件Dubbo、规则引擎Apache Camel、敏捷Java开发框架Spring Boot、JDBC 规范都用到了SPI机制。接下来我们通过一个DEMO来认识它。
2. SPI 的需求场景
假如你是一家产品上游企业,你希望你的厂商都能按照你的标准去生产下游组件,所以你制定了一个驱动规范。厂商只要按照你的规范去实现驱动,那么就能对接上你的应用产品。所以SPI的开发流程大致是这样的:
Java SPI 开发流程
试想一下 JDBC规范不就是这样的吗?
3. SPI 开发
按照上面的需求场景,我们来模拟一下SPI开发流程。
3.1 上游厂商需要做的事情
制定接口规范,对于Java来说就是抽象出一些接口并提供给下游厂商。这里简单写一个接口:
/**
* @author felord.cn
* @since 9:55
**/
public interface ApiService {
void execute();
}
然后编写接口的发现相关逻辑,Java提供了java.util.ServiceLoader<S>
来进行SPI接口的加载,非常简单。需要指出的是它实现了可迭代接口Iterable<E>
,因为如果下游厂商编写了多个实现并注册也要保证能获取到,当然这也有一定的弊端,后面会谈这个事情。针对上面的接口发现逻辑是非常简单的:
import java.util.ServiceLoader;
/**
* @author felord.cn
* @since 13:06
**/
public class ApiDiscovery {
public static void doDiscovery(){
ServiceLoader<ApiService> load = ServiceLoader.load(ApiService.class);
load.forEach(ApiService::execute);
}
}
这样上游的事就干完了,打成 jar 生成依赖提供给下游去实现。
<dependency>
<groupId>cn.felord</groupId>
<artifactId>service-api</artifactId>
<version>1.0</version>
</dependency>
其中发现这一部分也可以不提供给下游。
3.2 下游厂商需要做的事情
下游厂商集成上游厂商提供的依赖:
<dependency>
<groupId>cn.felord</groupId>
<artifactId>service-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
开始实现接口规范:
import cn.felord.spi.ApiService;
/**
* @author felord.cn
* @since 10:24
**/
public class SpiProvider implements ApiService {
@Override
public void execute() {
System.out.println(" hello provider ");
}
}
实现完成之后就要进行SPI注册了。步骤是这样的:
- 在classpath下建立
META-INF/services
目录, - 在 1 步骤建立的
META-INF/services
目录下创建名称为规范抽象接口(例如上面的cn.felord.spi.ApiService
)的文件。 - 在文件里把对应文件名称抽象接口的实现类的全限定名写进去,有多个的话换行。
SPI实现注册
这样就下游就开发完毕了。同样提供了一个包给调用方去调用;
<dependency>
<groupId>cn.felord</groupId>
<artifactId>provider</artifactId>
<version>1.0</version>
</dependency>
3.3 调用 SPI 实现
消费方调用只需要集成规范的实现去调用发现接口的逻辑对象ApiDiscovery
就可以了:
<dependency>
<groupId>cn.felord</groupId>
<artifactId>provider</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>cn.felord</groupId>
<artifactId>service-api</artifactId>
<version>1.0</version>
</dependency>
4. SPI 的优缺点
SPI的优点:实现了解耦,不再通过import
关键字导入实现,而是通过动态的加载实现。使得业务组件插件化。
SPI的缺点:ServiceLoader在加载实现类的时候会全部加载并实例化,无论你是否需要使用到它。而且只能通过迭代去获取实现,无法通过关键字来获取。
5. 总结
今天通过一个简单的例子演示了Java SPI特性的使用,这对开发驱动、模块化、插件化非常有用。配置注册SPI是一个非常细心的过程,如果我们不小心配置错误就无法达到预期。所以谷歌提供了一个AutoService的SPI增强包,有兴趣的话可以去了解一下,非常简单,这里不再讲解。相关 DEMO 可通过本公众号回复spi获取。多多关注:码农小胖哥 更多干货分享。
- grpc部署初体验
- Java中的ReentrantLock和synchronized两种锁机制的对比
- 用Python从零开始创建区块链
- 基于 Python 的僵尸网络将 Linux 机器变成挖矿机器人
- Oracle导入导出常用命令
- Spring Cloud实战小贴士:Zuul处理Cookie和重定向
- 设计模式之代理模式(二)CGLIB动态代理实现
- ios手势复习值之换图片-转场动画(纯代码)
- 顺序广播和无序广播
- Netflix Zuul与Nginx的性能对比
- 最有价值的50道java面试题(一)
- 用 Python 从零开始玩微信跳一跳
- ios地图小例子和手势的使用 供大家参考一下呦
- Spring Cloud源码分析(四)Zuul:核心过滤器
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 3D图形学线代基础
- Splash抓取jd
- codeforces 1395C(暴力枚举)
- 不到100行代码搞定Python做OCR识别身份证,文字等各种字体
- codeforces 1389B(贪心)
- 又一个自动生成项目目录组件tree-cli,快速生成Readme项目结构
- 用Vue CLI创建uni-app,摆脱HBuilder,npm命令行运行及发布
- codeforces 1133D(map+精度控制)
- 1024程序员节 | 我在腾讯自研数据库,我为技术代言
- jasmine spyOn的单步调试
- codeforces 1363C(优先队列)
- 系统与应用异常定位诊断
- SQL优化终于干掉了“distinct”
- SAP Spartacus delivery mode页面Cannot find control with的错误消息
- codeforces 1349A(数学)