在java中使用SPI创建可扩展的应用程序
程序那些事
简介
什么是可扩展的应用程序呢?可扩展的意思是不需要修改原始代码,就可以扩展应用程序的功能。我们将应用程序做成插件或者模块。
这样可以在不修改原应用的基础上,对系统功能进行升级或者定制化。
本文将会向大家介绍如何通过java中的SPI机制实现这种可扩展的应用程序。
SPI简介
SPI的全称是Java Service Provider Interface。是java提供的一种服务发现的机制。
通过遵循相应的规则编写应用程序之后,就可以使用ServiceLoader来加载相应的服务了。
SPI的实现主要分为4个部分:
Service Provider Interface: SPI是一个interface或者是抽象类,其中定义了我们需要扩展实现的功能。
Service Providers:这是SPI的具体实现,提供了具体的实现功能
SPI Configuration File:SPI的配置文件,通过在配置文件我们来配置相关的SPI发现信息。
ServiceLoader: ServiceLoader是用来加载和发现服务的java类,并提供了很多有用的方法。
SPI的普通java实现
讲完SPI的定义,大家可能还是不清楚SPI到底是做什么的,又该怎么使用它。
不用急,我们下面通过一个例子来说明。
首先创建一个module:SPI-service,里面主要定义了一个ModuleService接口:
public interface ModuleService {}
然后再分别创建两个module,作为ModuleService的实现:
public class ModuleServiceA implements ModuleService {
public ModuleService getModuleService(){
return new ModuleServiceA();
}}
public class ModuleServiceB implements ModuleService {
public ModuleService getModuleService(){
return new ModuleServiceB();
}}
接着分别在两个module中创建META-INF/services文件夹,并且在里面创建两个以 Service Provider Interface限定名为名字的文件,这里文件名是:com.flydean.base.service.ModuleService,文件里面存放的是SPI的具体实现类:
com.flydean.base.servicea.ModuleServiceA
com.flydean.base.serviceb.ModuleServiceB
最后,我们需要创建一个使用SPI的类:
public class ModuleController {
public static void main(String[] args) {
List<ModuleService> moduleServices = ServiceLoader
.load(ModuleService.class).stream()
.map(ServiceLoader.Provider::get)
.collect(toList());
log.info("{}", moduleServices);
}}
为了更好的展示扩展应用的实际使用,我们分别创建4个模块。在实际应用中,只需要将这些jar包加入应用程序的classpath即可。
运行看下输出结果:
[com.flydean.base.servicea.ModuleServiceA@16f65612,
com.flydean.base.serviceb.ModuleServiceB@311d617d]
从结果看到,我们获得了两个ModuleService。证明系统扩展成功。
SPI在JPMS模块化系统下的实现
上面我们讲的是基本的操作,考虑一下,如果是在JDK9之后,引入了JPMS模块化系统之后,应该怎么使用SPI呢?
代码肯定是一样,我们需要修改的是SPI配置文件。
如果在JPMS中,我们就不需要使用META-INF/services了,我们只需要创建相应的module-info.java文件即可。
先看下SPI模块的module-info.java文件:
module com.flydean.service {
exports com.flydean.service;}
这个模块我们对外暴露了service package,供其他模块调用。
接下来是SPI的实现模块:
module com.flydean.servicea {
requires com.flydean.service;
provides com.flydean.service.ModuleService with com.flydean.servicea.ModuleServiceA;
exports com.flydean.servicea;}
这里我们使用了provides命令,定义了两个类的关联关系。
最后是调用的模块:
module com.flydean.controller {
uses com.flydean.service.ModuleService;
requires com.flydean.service;
requires lombok;
requires slf4j.api;}
这里我们使用uses关键词来引用ModuleService。
总结
本文介绍了SPI在模块化和非模块化系统中的应用。
本文中的例子:https://github.com/ddean2009/learn-java-base-9-to-20
作者小F,金融科技从业多年,懂技术又懂金融,主攻Java和区块链方向,篇篇都是用心之作,笔耕不辍,持续更新!
- 结合Scikit-learn介绍几种常用的特征选择方法(下)
- Spring Cloud Bus之RabbitMQ初窥
- 结合Scikit-learn介绍几种常用的特征选择方法(上)
- 判断图片是否加载完成
- Spring Cloud Config客户端配置细节
- Spring Cloud Config服务端配置细节(二)之加密解密
- Redis3 cluster 集群配置测试
- Spring Cloud Config服务端配置细节(一)
- 分布式配置中心Spring Cloud Config初窥
- Git标签管理
- Docker容器如何共享数据
- Git工作区储藏兼谈分支管理中的一个小问题
- 轻量级容器Docker - 创建nginx容器
- Git关联远程仓库
- 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 文档注释
- springcloud本地开发的微服务如何调用远程k8s的微服务
- Flutter基础widgets教程-LimitedBox篇
- 算法:滑动窗口(二)
- Flutter基础widgets教程-LinearProgressIndicator篇
- Flutter基础widgets教程-ListTile篇
- 深入剖析 linux GCC 4.4 的 STL String
- Flutter基础widgets教程-MaterialApp篇
- 如何使用Visual Studio Code开发Django项目
- Flutter基础widgets教程-Padding篇
- Flutter基础widgets教程-Placeholder篇
- Flutter基础widgets教程-PopupMenuButton篇
- Python部署rad+xray自动化
- 2.建立第一个django项目与配置
- Flutter基础widgets教程-Radio篇
- Django中的QuerySet