一文搞懂Java的SPI机制

news/2025/3/28 19:47:29/文章来源:https://www.cnblogs.com/liftsail/p/18794239

1 简介

SPI,Service Provider Interface,一种服务发现机制。

有了SPI,即可实现服务接口与服务实现的解耦:

  • 服务提供者(如 springboot starter)提供出 SPI 接口。身为服务提供者,在你无法形成绝对规范强制时,适度"放权" 比较明智,适当让客户端去自定义实现
  • 客户端(普通的 springboot 项目)即可通过本地注册的形式,将实现类注册到服务端,轻松实现可插拔

缺点

  • 不能按需加载。虽然 ServiceLoader 做了延迟加载,但是只能通过遍历的方式全部获取。如果其中某些实现类很耗时,而且你也不需要加载它,那么就形成了资源浪费
  • 获取某个实现类的方式不够灵活,只能通过迭代器的形式获取

Dubbo SPI 实现方式对以上两点进行了业务优化。

源码

应用程序通过迭代器接口获取对象实例,这里首先会判断 providers 对象中是否有实例对象:

  • 有实例,那么就返回
  • 没有,执行类的装载步骤,具体类装载实现如下:

LazyIterator#hasNextService 读取 META-INF/services 下的配置文件,获得所有能被实例化的类的名称,并完成 SPI 配置文件的解析

LazyIterator#nextService 负责实例化 hasNextService() 读到的实现类,并将实例化后的对象存放到 providers 集合中缓存

使用

如某接口有3个实现类,那系统运行时,该接口到底选择哪个实现类呢? 这时就需要SPI,根据指定或默认配置,找到对应实现类,加载进来,然后使用该实现类实例

如下系统运行时,加载配置,用实现A2实例化一个对象来提供服务:

再如,你要通过jar包给某个接口提供实现,就在自己jar包的META-INF/services/目录下放一个接口同名文件,指定接口的实现是自己这个jar包里的某类即可:

别人用这个接口,然后用你的jar包,就会在运行时通过你的jar包指定文件找到这个接口该用哪个实现类。这是JDK内置提供的功能。

我就不定义在 META-INF/services 下面行不行?就想定义在别的地方可以吗?

No!JDK 已经规定好配置路径,你若随便定义,类加载器可就不知道去哪里加载了

假设你有个工程P,有个接口A,A在P无实现类,系统运行时怎么给A选实现类呢? 可以自己搞个jar包,META-INF/services/,放上一个文件,文件名即接口名,接口A的实现类=com.javaedge.service.实现类A2。 让P来依赖你的jar包,等系统运行时,P跑起来了,对于接口A,就会扫描依赖的jar包,看看有没有META-INF/services文件夹:

  • 有,再看看有无名为接口A的文件:
    • 有,在里面查找指定的接口A的实现是你的jar包里的哪个类即可

适用场景

插件扩展

比如你开发了一个开源框架,若你想让别人自己写个插件,安排到你的开源框架里中,扩展功能时。

如JDBC。Java定义了一套JDBC的接口,但并未提供具体实现类,而是在不同云厂商提供的数据库实现包。

但项目运行时,要使用JDBC接口的哪些实现类呢?

一般要根据自己使用的数据库驱动jar包,比如我们最常用的MySQL,其mysql-jdbc-connector.jar 里面就有:

系统运行时碰到你使用JDBC的接口,就会在底层使用你引入的那个jar中提供的实现类。

案例

如sharding-jdbc 数据加密模块,本身支持 AES 和 MD5 两种加密方式。但若客户端不想用内置的两种加密,偏偏想用 RSA 算法呢?难道每加一种算法,sharding-jdbc 就要发个版本?

sharding-jdbc 可不会这么蠢,首先提供出 EncryptAlgorithm 加密算法接口,并引入 SPI 机制,做到服务接口与服务实现分离的效果。 客户端想要使用自定义加密算法,只需在客户端项目 META-INF/services 的路径下定义接口的全限定名称文件,并在文件内写上加密实现类的全限定名

这就显示了SPI的优点:

  • 客户端(自己的项目)提供了服务端(sharding-jdbc)的接口自定义实现,但是与服务端状态分离,只有在客户端提供了自定义接口实现时才会加载,其它并没有关联;客户端的新增或删除实现类不会影响服务端
  • 如果客户端不想要 RSA 算法,又想要使用内置的 AES 算法,那么可以随时删掉实现类,可扩展性强,插件化架构

引用自

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/906241.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EDA 学习笔记之 def 文档笔记1:基础介绍

介绍 def 文件的一些基本内容@目录def 文件介绍基本定义VIAS 定义NDR 定义COMPONENTPINSBLOCKAGESSPECIAL NETSNETSFILLS def 文件介绍 DEF: Design Exchange Format , 描述了数字电路在布局布线后的连接关系和位置关系,是将数字实现前后端连接起来的桥梁。 目前常用的 def ve…

day01计算机基础

计算机三层结构软件开发流程运维职责 1.软件724365运行 ==>监控 2.数据备份 3.优化 计算机组成 控制器 运算器 存储器 输入输出设备cpu 控制和运算 存储器 存储器之内存 1.内存条相当于人脑的记忆功能,只能临时存放数据 2.内存里存放的都是电信号,断电数据则丢失,相当于人…

甘特图项目进度管理必备:里程碑功能设置与透明化实践

你是否因为项目无法常常密切关注每个任务细节且耗时耗力?日事清里程碑让你轻松又高效!你听过山田本一这个名字吗? 他是日本的马拉松选手,曾在国际马拉松比赛中获得冠军。 他每次比赛前,都要仔细研究赛道,记下每个阶段的标志物,甚至把40多公里的赛程分解成几个小目标。 你…

绩效考核如何从形式化任务升级为公司战略工具?

竟然还有很多人误以为绩效管理只是用来“压榨”员工的???年底了,又到了各家公司对员工做年终绩效考评的时候。 很多人误以为绩效管理只是用来“压榨”员工的, 也有不少人觉得,绩效管理就是“一个成绩单”,搞得好像只为了那一两千块钱绩效工资才做的。 就连一些企业高层,…

OKR如何破解企业四大割裂难题:战略落地与组织协同的实战指南

企业常见战略传递失真、部门协作不畅、执行脱节及市场响应滞后四大管理痛点。日事清以OKR对齐目标、看板协同资源、PDCA闭环管控和敏捷复盘机制,系统化贯通战略到执行的完整链路,实现内外高效协同。在走访企业的过程中,我们发现很多企业在市场竞争非常激烈的情况下,维持业务…

微信公众号---API接口发布文章

最近在做一个项目,需要用到通过API接口发布推文。 基本流程: 1、注册微信公众号(订阅号和服务号都可以) 2、开发配置获取 appid 以及 appsecret,以及配置服务器IP白名单。 3、开发流程:接口需要 access_token 第一步:上传推文的素材,主要是推文的封面以及文章所需要的图…

航航(和一些人讲的题目)

这里标题是 hangjsmh 要求的题解写的非常简略,甚至一道题细节都没有清楚。 AGC067D 首先设排列为 \(1\sim n\) 是可行的。 第一感觉是相邻不可交换,即如果 \(l_i<i\),则 \(r_{i-1}=i-1\)。进一步地,\(r_{l_i\sim i}<i\),如果满足这个条件也易见其唯一性。 画在二维平…

win11自带录屏工具

Setp1: 打开设置Setp2: 打开游戏->摄像Setp3: 配置录制参数Setp4: 开始录像 WIN+Alt+GSetp5: 打开录像文件

杭州储存卡数据恢复之雷克沙短路损坏不识别售后维修失败二次修复

这是一张64G的Lexar雷克沙CF接口内存卡,型号是1066X,CANON佳能5D4相机使用的,这张存储卡是硬件出问题了无法识别,说是可能插卡时角度不对用力顶入后使顶针VCC脚变形短路,通电后导致芯片损坏。客户先寄修到雷克沙官方售后那边进行维修,但最终检测修复失败,说是短路严重,…

RabbitMQ核心架构

Producer:负责产生消息。 Connection:RabbitMQ客户端和代理服务器之间的TCP连接。 Channel:建立在连接之上的虚拟连接,RabbitMQ操作都是在信道中进行。 Broker:一个Broker可以看做一个RabbitMQ服务节点或者服务实例。 Exchange:生产者发送消息到交换器,交换器根据路由ke…

024 登录页-main退出登录功能的实现

这个页面这样写一、 用于创建一个按钮并绑定一个点击事件处理函数。以下是对这段代码的详细解释:<button> 标签:这是 HTML 中的按钮元素,用于在页面上显示一个可点击的按钮。@click 指令:在 Vue.js 中,@click 是一个事件绑定指令,它用于监听按钮的点击事件。@ 是 …