首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca…
一 . 前言
虽然是一个老概念 , 但是都怪自己的轻微强迫症 , 有个点没搞清楚 , 就非要把这个概念翻出来看看, 这一篇是对 Java SPI 概念的一个完善 , 为后续的 Dubbo 等框架的分析做准备
Java SPI 是 JDK提供的SPI(Service Provider Interface)机制 , SPI 机制的核心在于 ServiceLoader , 用于加载接口对应的实现类
用一句话来解释 , 就是以 Java 的方式查找接口对应的实现类 , 实现解耦 (区别于 Spring 里面的Bean工具 , SPI 的方式更加底层)
二 . 知识点
SPI 机制中有四个组成部分 :
- Service Providers : 服务器供应商
- Installing Service Providers : 安装服务供应商
- Loading Service providers : 装载服务供应商
- Service Loader : 服务承载程式
Service Provider : 服务提供者
Service Provider 是 SPI 的特定实现。服务提供者包含一个或多个实现或扩展服务类型的具体类 , 服务提供者是通过我们放在资源目录 META-INF/services 中的提供者配置文件来配置和标识的
ServiceLoader
ServiceLoader 是 SPI 的核心类 , 它的作用是发现和惰性加载实现 , 它使用上下文类路径来定位提供程序实现并将它们放在内部缓存中。
常用的 SPI 类
- CurrencyNameProvider : 为currency类提供本地化的货币符号
- LocaleNameProvider : 为Locale类提供本地化名称
- TimeZoneNameProvider : 为TimeZone类提供本地化的时区名称
- DateFormatProvider : 提供指定区域的日期和时间格式
- NumberFormatProvider : 为NumberFormat类提供货币值、整数和百分比值.
- Driver : 从4.0版本开始,JDBC API支持SPI模式
- PersistenceProvider : 提供JPA API的实现。
- JsonProvider : 提供JSON处理对象
- JsonbProvider : 提供JSON绑定对象
- Extension : 为CDI容器提供扩展
- ConfigSourceProvider : 提供用于检索配置属性的源
三 . SPI 的使用
SPI 的使用中我分成了三个包 :
1 | java复制代码// provider-api : API 描述包 , 包含 Provider 接口 |
3.1 provider-api 包
1 | java复制代码// Step 1 : Provider 接口 , 我们用它返回一个通用的业务类 |
3.2 provider-impl
1 | java复制代码// 实现类 |
3.3 Server applicaiton
1 | java复制代码public class StartService implements ApplicationRunner { |
PS : 这里有个很重要的东西 , 你需要 META-INF/services 中添加对应的文件
- 文件名 : Provider 接口
- 文件内容 : 涉及到的实现类
这个文件放在 impl 和 app 中都行 , 实际上引包了就会扫描
1 | xml复制代码<dependency> |
PS : 内容已提交到 Git , 欢迎 Star !!!! 👉 Case/java/spi
四 . SPI 源码深入
如果就这么结束当然不符合我一贯的做法 , 源码还是要看一下的, 别说 , 还真有一些启发
4.1 运行的走向
1 | java复制代码// Step 1 : 发起 Providers 加载操作 |
4.2 属性
1 | java复制代码// 默认从 META-INF/services/ 路径下加载 |
4.3 resource 的加载
这里的核心是做了一个定制的实现类 LazyIterator
前面看了 ServiceLoader 的构建 , 这里来看一下 resource 的加载
1 | java复制代码// 这里不需要深入太多 , 主要是资源加载处理 Service 类 |
4.4 load 处理加载 Class
1 | java复制代码// 迭代器迭代 |
PS: 核心就2个 ,一个是hasNext 中加载 resource , 再在 nextService 中实例化对应的server
这样的好处是 , 只有在正在使用的时候 , 才会真的去实例化这个对象 !!!
五 . 定制与比较
这里主要对比 Spring SPI 的加载方式 , 详见这一篇 盘点 SpringBoot : Factories 处理流程
文件的配置方面 : Spring 中使用的是 SpringFactoriesLoader , 其实与 Java 的方式是很像的 , 但是 Spring 的模式下 , 允许一个 factories 文件装载更多的类 , 使用更加简单 .
资源的加载方法 : 2 者都是通过 classLoader 加载 Resource , 并没有太大本质的区别
而资源的实例化方面 : Spring 通过一个 instantiateFactory 方法触发 ,但是同样的 , 也是 class 反射的原理
高并发情况 : 在调用 hasNext 的时候 , 加载 resource , 在迭代时才实例化 看起来好像没有什么问题 , 但是其 classLoader , provider 都是放在 ServiceLoader 对象属性中 , 多线程情况下会存在冲突
而 Spring 的模式 , 在 Server 启动时 , 就加载对象 Factories , 相对安全很多.
总得来说 , Spring Factories 的 Java SPI 的逻辑思路是一致的 , Java SPI 通过 LazyIterator 加载的方式比较骚气 ,但是相对而言获取起来就会很复杂 .
所以 , 完全可以使用 Spring Factories 来完成你自己想要的业务.
总结
定制 Iterator 完成业务是一个不错的思路
参考与感谢
本文转载自: 掘金