您当前的位置:首页 > 电脑百科 > 程序开发 > 程序员

某厂面试:如何优雅使用 SPI 机制

时间:2021-06-01 11:56:02  来源:今日头条  作者:Java识堂

原文:某厂面试:如何优雅使用 SPI 机制

作者:龙台

代码不多,文章可能有点长。朋友面试某厂问到的 SPI 机制,联想到自己项目最近写到的 SPI 场景,文章简要描述下 SPI 机制的发展历程

产出背景

因为最近项目中使用分库分表以及数据加密使用到了 ShardingSphere,所以决定这段时间看看源码实现。问我为什么要读源码?不看源码怎么提高逼格嘞,就是这么朴实无华~

考虑到自己看微信文章的习惯,不喜欢代码太多的,看着逻辑有点不清晰。所以,以后的文章风格就是,少贴代码,画图 + BB

Sharding-Jdbc SPI

看源码的历程,往往从点开 Jar 包的瞬间开始。好巧不巧,就看到源代码包下有个 SPI 包,处于好奇心就点了一点,嗯~ 代码果然很熟悉,还是那个配方原来的味道

某厂面试:如何优雅使用 SPI 机制

 

看了许久,陷入深深的沉思。内心小九九:这玩意好像之前看过,但是在哪我忘了,这到底是个啥?

代码还是那个代码,只是它认识我,我不认识它了

这一块的 SPI 接口是 shrding-jdbc 预留自定义加密器的接口

看到这里相信就遇到过绝大多数技术同学都会遇到的一个问题,那就是 认为自己会了,实际情况呢?不一定。所以,学习一门技术,一定要多看几遍,尝试去理解记忆。千万不要看一遍之后,眼高手低认为技术 so easy,然后隔十天半个月就啥都不记的

 

继续回过头来说说今天的主角:SPI。首先回答这么一个问题,什么是 SPI 机制

SPI 全称为 Service Provider Interface,是一种服务发现机制。为了被第三方实现或扩展的 API,它可以用于实现框架扩展或组件替换

SPI 机制本质是将 接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载文件中的实现类,这样运行时可以动态的为接口替换实现类

看文字描述介绍总是枯燥无味且空洞的。简单一点来说,就是你在 META-INF/services 下面定义个文件,然后通过一个特殊的类加载器,启动的时候加载你定义文件中的类,这样就能扩展原有框架的功能

就这么简单,那可能有读者会问:我不定义在 META-INF/services 下面行不行?就想定义在别的地方

 

不行滴,请遏制住这么危险的想法,人家怎么定义你就怎么实现。这是 JDK 规定好的配置路径,你随便定义,类加载器怎么知道去哪里加载

某厂面试:如何优雅使用 SPI 机制

 

看到这个 PREFIX 常量之后,想法比较活跃的小伙子不知道清醒点了么。简单画张图来描述下 SPI 的运行机制

某厂面试:如何优雅使用 SPI 机制

 

有点 SPI 基础的同学看到图之后应该又开始自信了,这不就是我之前看过的那玩意么?是的,技术还是那个技术,可以继续往下看看,有没有自己不知道的

为什么要有 SPI

了解一项技术的前提,一定要知道它为了解决什么样的痛点而存在,JDK 作者也不会没屁事加点代码玩

引入了 SPI 机制后,服务接口与服务实现就会达成分离的状态,可以实现 解耦以及程序可扩展机制。服务提供者(比如 springboot starter)提供出 SPI 接口后,客户端(平常的 springboot 项目)就可以通过本地注册的形式,将实现类注册到服务端,轻松实现可插拔

数据加密举例

以实际项目举个例子,就拿 sharding-jdbc 数据加密模块来说,sharding-jdbc 本身支持 AES 和 MD5 两种加密方式。但是,如果客户端不想用内置的两种加密,偏偏想用 RSA 算法呢?难道每加一种算法,sharding-jdbc 就要发个版本么

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

某厂面试:如何优雅使用 SPI 机制

 

通过 SPI 的方式,就可以将客户端提供的加密算法加载到 sharding-jdbc 加密规则中,这样就可以在项目运行中选择自定义算法来对数据进行加密存储

通过 sharding-jdbc 的例子,可以很好的看出来,上面提到的 SPI 优点,都体现了出来

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

配合实际案例理解 SPI 是不是很简单。为了防止有些小伙伴没有理解 sharding-jdbc 的例子,这里再举一个真实的例子

对象存储举例

假如你是一家集团公司里做公共架构开发的(可以把这个集团想大一点,几百家子公司的那种 ️ ),领导给你安排了个开发任务,需要你开发一个对象存储服务,让其它业务线的团队使用,统一集团内部的对象存储

OK,开发诉求明白了,这个时候就该想想怎么去完成这个需求(主要想给领导留个好印象,升官发财 ing...)。首先应该考虑的是要兼容多套对象存储供应商,比如阿里 OSS、腾讯 COS、华为云 OBS,最基本的三连对吧

高高兴兴的封装了个 starter,告诉领导封装完成了,然后就下发到各项目组去用了。但是这个时候其中一个子公司负责人告诉你,说他们之前用的七牛云 Kodo

心态炸了呀,难道要给他再适配一个七牛云么?万一适配完这个,又一位大哥说项目自建 HDFS 咋整

聊到这,大家就明白了吧,SPI 的场景可不就出现了么。就是身为服务提供者,在你无法形成绝对规范强制的时候,"放权" 往往是比较明智的选择,适当让客户端去自定义实现

这个时候,回过头想一想最初的一个问题。为什么 sharding-jdbc 不多实现几套算法,而是提供出一个 SPI 接口呢

因为开发者明白,不论提供多少接口,总有个别用户因各方面因素导致的个性化需求。个性化这个事情是追摸不透的,就像 女生的心思一样,永远不知道在想什么...(重点都加黑加粗了,剩下的全靠自己领悟)

 

实战讲解

都说到这了,不来个实战,感觉有点说不过去。吹过的牛逼,负责到底!就实现上面说的统一对象存储服务的代码

最简单的对象存储,只需要两个接口就可以实现功能,分别是 上传和下载

某厂面试:如何优雅使用 SPI 机制

 

定义好上传、下载接口后,我们就要考虑,如何让客户端项目可以选择底层的对象存储服务器,以及如何通过 SPI 的方式将客户端自定义的文件存储组件加载到服务端

我们可以定义个对象存储容器,存放可以使用的对象存储服务,然后再 使用 SPI 的机制加载客户端自定义组件放到容器。对象存储服务放到容器中自然需要一个标识,那么就需要给文件接口加一个获取类型接口

某厂面试:如何优雅使用 SPI 机制

 

定义好了接口,就要写具体的代码了。我们为 对象存储服务提供出一个对外的门面,所有访问对象存储的服务,必须访问门面对象进行文件的上传下载操作

下面这段代码将 对象服务 bean 存储至容器,并提供根据客户端的自定义配置,选择合适的对象存储服务

代码里用到的关键字 var 是 lombok 的注解,可以自动识别对象类型

某厂面试:如何优雅使用 SPI 机制

 

因为是个示例 demo,所以将获取对象存储和具体的上传、下载耦合在了一起,如果小伙伴有类似需求,一定要将不同行为拆分开,类职责尽量单一些

这段代码整体逻辑不算复杂,所以也有点自信回头,就没跑单元测试,不过问题应该不大。解释一下其中具体逻辑:

  1. FileServiceFactory 大家可以理解为文件服务对外的统一访问入口。实现了 spirng 初始化的一个接口,可以在 bean 初始化时进行代码逻辑操作
  2. bean 初始化时,通过 ServiceLoader 类加载器负责加载对象存储接口,这样就能加载到客户端存放到 META-INF/services 中的自定义对象存储实现
  3. 获取到自定义对象存储后,和服务端本身自带的对象存储一起存放至容器中,这样就可以根据项目中的 fileStoreType 获取对应的服务了

结合实际的项目场景,一个简简单单的 SPI 应用就完成了,自我感觉比 JDBC 装配的例子更好理解一些

上面的业务只是为了让不理解 SPI 的小伙伴更好的掌握应用场景,其实对象存储服务是一种可穷举的业务场景,SPI 并不是唯一的解决思路。当然,为了省事使用 SPI 也没啥问题。最后提一句,SPI 最合适的还是没有统一业务实现场景,就像上面提到过的加密算法

深入解析 SPI

一篇技术解析文章,适当放一些源码解析感觉会更好一些。下面一起来看看 ServiceLoader底层都做了什么事情

通过 ServiceLoader 的 load 方法创建一个新的 ServiceLoader,并实例化其中的成员变量

某厂面试:如何优雅使用 SPI 机制

 

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

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

  1. LazyIterator#hasNextService 读取 META-INF/services 下的配置文件,获得所有能被实例化的类的名称,并完成 SPI 配置文件的解析
  2. LazyIterator#nextService 负责实例化 hasNextService() 读到的实现类,并将实例化后的对象存放到 providers 集合中缓存
某厂面试:如何优雅使用 SPI 机制

 

如果你不知道上面的一些 "黑话" 不要紧,因为都是 ServiceLoader 底层执行的方法,跟着下面这个程序敲一遍代码就懂了

某厂面试:如何优雅使用 SPI 机制

 

这里为了跟源码,也是把上面对象存储的逻辑,简单写了个 SPI 示例,证明是没有问题的。如果小伙伴想真正了解,就需要跟下源码去看看,其它源码部分就不细说了

结言

上面说了很多关于 SPI 机制的优点以及应用场景,这里总结下关键内容

  1. SPI 机制优势就是解耦。将接口的定义以及具体业务实现分离,而不是和业务端全部耦合在一端。可以实现 运行时根据业务实际场景启用或者替换具体组件
  2. SPI 机制的场景就是 没有统一实现标准的业务场景。一般就是,服务端有标准的接口,但是没有统一的实现,需要业务方提供其具体实现。比如说 JDBC 的 JAVA.sql.Driver 接口和不同云厂商提供的数据库实现包

每个事物都是既有优点,同时也伴随着缺点。要从两个方面去看,不能总盯着一方面。这里说一下 SPI 机制的缺点

  1. 不能按需加载。虽然 ServiceLoader 做了延迟加载,但是只能通过遍历的方式全部获取。如果其中某些实现类很耗时,而且你也不需要加载它,那么就形成了资源浪费
  2. 获取某个实现类的方式不够灵活,只能通过迭代器的形式获取。这两点可以参考 Dubbo SPI 实现方式进行业务优化

文章通过图文并茂的方式帮助大家重新梳理了一遍 SPI 的场景、优势和缺点,看完文章后相信大家对 SPI 机制有了更深入的认识

梳理出 SPI 的场景以及优势后,小伙伴最好再去 Debug 源代码,这样会大家对 SPI 的实现才能更加清楚。只有对一个知识点真正掌握,才不至于事后很快遗忘

另外可以通过项目中的场景,比如文中提到的加密、对象存储,通过类比的方式结合项目逻辑去实现代码代入,这样能够更好的去学习以及扩展相关的设计思路

创作不易,文章看到这里如果有所帮助,可以点个关注支持一下,祝好。我们下期见!



Tags: SPI 机制   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
因为最近项目中使用分库分表以及数据加密使用到了 ShardingSphere,所以决定这段时间看看源码实现。问我为什么要读源码?不看源码怎么提高逼格嘞,就是这么朴实无华~...【详细内容】
2021-06-01  Tags: SPI 机制  点击:(148)  评论:(0)  加入收藏
作者:crossoverJie前言不知大家现在有没有去公司复工,我已经在家办公将近 3 周了,同时也在家呆了一个多月;还好工作并没有受到任何影响,我个人一直觉得远程工作和 IT 行业是非常...【详细内容】
2020-02-11  Tags: SPI 机制  点击:(45)  评论:(0)  加入收藏
▌简易百科推荐
JAVA开发工程师(北京)本科 3-5年经验 面议 (招1人)岗位职责:1.负责我行应用系统的设计,完成软件编码工作,负责管理代码设计规范等工作;2.根据应用需求分析说明书,评估需求研发的可行...【详细内容】
2021-12-27  just do丶IT公众号    Tags:国企   点击:(0)  评论:(0)  加入收藏
今天聊聊编程的本质。程序就是数据结构+控制+逻辑,程序员编程工作的本质是翻译,翻译机要来了,程序员怎么办?黑客帝国中的程序黑客帝国4就要上映了,不知道前三部你看懂了么?值得多...【详细内容】
2021-12-17  博士聊IT    Tags:程序员   点击:(8)  评论:(0)  加入收藏
梦醒之后,每个人对于这份职业的未来、互联网行业的未来,以及更重要的,自己的未来都有了更现实的判断 文 | 祝颖丽编辑 | 黄俊杰一个生于 1986 年的人,他所走过的前半生:从出生起,...【详细内容】
2021-12-03    财经杂志  Tags:程序员   点击:(15)  评论:(0)  加入收藏
前些天在头条看到一个八二年的哥们,述说自己找工作屡次被拒的问题,在网上引起了广泛的讨论,这件事给我留下了很深的印象,因为这哥们和我同是程序员,都人到中年,上有老下有小。唯一...【详细内容】
2021-12-01  云南贤哥在深圳    Tags:程序员   点击:(20)  评论:(0)  加入收藏
很多读者都问过一个问题:程序员如何实现高速成长?之前也写过相关的文章,强调的主要是夯实计算机体系基础知识。 再说另一个诀窍:多看经典开源项目,这些项目大多是众多顶尖程序员...【详细内容】
2021-11-30  findyi    Tags:程序员   点击:(14)  评论:(0)  加入收藏
近日,一位45岁的网民在中国政府网留言求职,引发关注。该网民自称是一名软件开发人员,今年45岁,精通各种技术体系,“而我辞职回家半年后再回来寻找工作机会的时候,却发现连个面试...【详细内容】
2021-11-17  郭主任    Tags:程序员   点击:(42)  评论:(0)  加入收藏
即使在安全技术取得进步之后,网络犯罪仍在不断增加。据统计,网络犯罪每分钟给企业造成约 290 万美元的损失。主要是因为新技术不断涌现,难以维护安全。随着网络威胁的增加,网络...【详细内容】
2021-11-04  章大千    Tags:编程语言   点击:(40)  评论:(0)  加入收藏
北漂小伙李强(化名),在北京互联网大厂工作7年,月薪3万,离职回老家开摄影店,亏了200万。李强出生于山西一座名不经传的小城市,互联网专业大学毕业的他,没有听父母的劝言回到家乡考公...【详细内容】
2021-10-29  霸王课  今日头条  Tags:程序员   点击:(51)  评论:(0)  加入收藏
程序员是青春饭,这在国内似乎是公认的。所以很多公司不愿招大龄程序员,很多程序员也“知趣”地及早转型。有的做管理,有的做架构,我还见过改行卖保险的。总之,年龄大了不想敲代码...【详细内容】
2021-10-27  编程的艺术    Tags:   点击:(29)  评论:(0)  加入收藏
软件工程专业有Web开发、移动终端开发、大数据开发、计算机系统工程师、视频开发工程师、计算机软件应用工程师等就业方向。第一,Web开发。Web开发包括前端开发和后端开发。...【详细内容】
2021-10-19  辰新   企鹅号  Tags:软件工程   点击:(79)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条