当前位置: 首页 > news >正文

浅谈JAVA中的SPI机制

一、SPI的机制定义

SPI,全称Service Provider Interface, 是Java中提供的一种服务发现机制,它允许应用程序动态地加载和使用第三方提供的服务实现,而无需在代码中显示引用这些实现类。通过将服务接口和其实现分离,从而具备更加好的可扩展性和可维护性。

Java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制,将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦。允许服务提供者通过实现调用者特定的接口来扩展系统,并且调用者能够动态地发现和加载这些服务提供者的实现类。

二、SPI的实现原理

2.1 Java SPI 实际上是"基于接口的编程+策略模式+配置文件"组合实现的动态加载机制。

2.2 实现SPI的步骤如下:

  1. 在服务提供者的JAR文件内,创建一个名为META-INF/services的目录。

  2. 在该目录下创建一个文件,文件名为服务接口的全限定名。

  3. 在该文件中列出服务接口的所有实现类。

2.3 ServiceLoader动态加载接口实现类的原理:类加载ClassLoader+迭代器模式

1. 初始化ServiceLoader类,传入自己想要加载的接口类
ServiceLoader<MyDriver> loader = ServiceLoader.load(MyDriver.class);2. 内部还是用的当前线程上下文的ClassLoader
public static <S> ServiceLoader<S> load(Class<S> service) {ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl);
}3. 创建懒加载迭代器LazyIterator
private ServiceLoader(Class<S> svc, ClassLoader cl) {service = Objects.requireNonNull(svc, "Service interface cannot be null");loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;reload();
}public void reload() {providers.clear();lookupIterator = new LazyIterator(service, loader);
}4. 通过遍历迭代器,寻找服务的实现类
private S nextService() {if (!hasNextService())throw new NoSuchElementException();String cn = nextName;nextName = null;Class<?> c = null;try {c = Class.forName(cn, false, loader);} catch (ClassNotFoundException x) {fail(service,"Provider " + cn + " not found");}if (!service.isAssignableFrom(c)) {fail(service,"Provider " + cn  + " not a subtype");}try {S p = service.cast(c.newInstance());providers.put(cn, p);return p;} catch (Throwable x) {fail(service,"Provider " + cn + " could not be instantiated",x);}throw new Error();          // This cannot happen
}private static final String PREFIX = "META-INF/services/";
5. 发现服务提供者的目录为fullName:"META-INF/services/"+服务类名
private boolean hasNextService() {if (nextName != null) {return true;}if (configs == null) {try {String fullName = PREFIX + service.getName();if (loader == null)configs = ClassLoader.getSystemResources(fullName);elseconfigs = loader.getResources(fullName);} catch (IOException x) {fail(service, "Error locating configuration files", x);}}while ((pending == null) || !pending.hasNext()) {if (!configs.hasMoreElements()) {return false;}pending = parse(service, configs.nextElement());}nextName = pending.next();return true;
}

三、SPI在框架中的使用场景

调用者根据实际使用需要,动态启用或扩展的实现策略

  • 日志门面接口实现类加载SLF4J加载不同提供商的日志实现类

  • 数据库驱动加载接口实现类的加载JDBC加载不同类型数据库的驱动

  • SpringBoot的SPI机制:我们可以在 spring.factories 中加上我们自定义的自动配置类,事件监听器或初始化器等;

四、实际使用案例:

  1. 比如我现在有一个获取业务方数据的功能,获取数据的方式和想要的数据信息由我自己决定,但是想要的业务方数据很多,而且数据提供方不确定,也不知道有多少。此时可以采用JAVA中的SPI机制思想,由自己决定获取数据的接口,未来服务方只需要提供这个接口的实现类即可。

调用方接口制定:

public interface ThirdPartyQueryService {/*** 获取第三方服务信息** @param projectId 项目id* @param infoId    信息id* @param userId    用户id* @return 信息*/ThirdPatyData queryThirdPartyData(Integer projectId, Long infoId, Long userId);
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 制作 Docker 镜像
  • 有关树形结构数据的功能函数
  • Uniapp 调用aar、jar包
  • 什么是Jmeter ?Jmeter使用的原理步骤是什么?
  • Cobalt Strike 4.8 用户指南-第五节-获取初始访问
  • [数据集][目标检测]玻璃瓶塑料瓶检测数据集VOC+YOLO格式8943张2类别
  • 猫咪浮毛清理措施?希喂、安德迈、有哈宠物空气净化器数据大揭秘
  • html+css+js网页设计 翘珠宝微商城移动端20个页面
  • 正则表达式实现带有条件的爬取
  • .net dataexcel winform控件 更新 日志
  • Linux - 深入探讨 Linux `ls` 命令:一个全面的技术指南
  • 【前端面试】采用react前后,浏览器-解析渲染UI的变化
  • cnocr 安装
  • OpenHarmony使用ArkUI Inspector分析布局
  • 一套高效、稳定的自卸车自动充电系统
  • 【5+】跨webview多页面 触发事件(二)
  • Android单元测试 - 几个重要问题
  • Android框架之Volley
  • Asm.js的简单介绍
  • Druid 在有赞的实践
  • egg(89)--egg之redis的发布和订阅
  • JavaScript的使用你知道几种?(上)
  • SpringBoot几种定时任务的实现方式
  • Spring核心 Bean的高级装配
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • vue-loader 源码解析系列之 selector
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 搞机器学习要哪些技能
  • 技术:超级实用的电脑小技巧
  • 全栈开发——Linux
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 如何借助 NoSQL 提高 JPA 应用性能
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 我从编程教室毕业
  • 协程
  • 主流的CSS水平和垂直居中技术大全
  • ​Java并发新构件之Exchanger
  • ​香农与信息论三大定律
  • (13):Silverlight 2 数据与通信之WebRequest
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (C++二叉树05) 合并二叉树 二叉搜索树中的搜索 验证二叉搜索树
  • (PADS学习)第二章:原理图绘制 第一部分
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (三)docker:Dockerfile构建容器运行jar包
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (算法二)滑动窗口
  • (五)Python 垃圾回收机制
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • (最新)华为 2024 届秋招-硬件技术工程师-单板硬件开发—机试题—(共12套)(每套四十题)
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .net core 连接数据库,通过数据库生成Modell
  • .Net Memory Profiler的使用举例
  • .net 调用php,php 调用.net com组件 --