Java SPI机制介绍及原理分析

news2025/1/11 6:29:23

概念介绍

SPI 即 Service Provider Interface ,字面意思就是:“服务提供者的接口”,我的理解是专门给服务提供者使用的接口,也就是定义接口的人,和实现接口的人并不是同一个人
SPI 将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。
单看概念比较抽象,下面用演示代码具象一点看看到底是怎么回事

代码演示

SPI的核心是JDK中的这个类ServiceLoader,可以加载当前环境中指定接口的实现。
这里接口定义者定义了一个日志服务接口LogService,代码如下

public interface LogService {
    void info(String info);
}

该接口定义了一个输出日志的方法,并且提供了一个打印日志的函数

public static void printLog(String info) {
   ServiceLoader<LogService> serviceLoader = ServiceLoader.load(LogService.class);
   for (LogService logService : serviceLoader) {
       logService.info(info);
   }
}

这里就用到了ServiceLoader加载当前环境中LogService的实现,并且调用接口的info方法去打印日志,如果这是我们调用printLog将什么也不会打印,因为接口定义者只定义了接口,但是并没有实现接口,所以拿到的serviceLoader里并没有实现类,之所以接口定义者没有去实现接口是因为他并不知道使用printLog打印日志的调用方希望把日志打印到哪里去,可以打印到控制台,也可以打印到文件,也可以把日志信息通过网络发出去。
这里出现了一个服务提供者他会去实现接口,并且调用printLog打印日志实现他自己对日志输出位置的需求。
这时服务提供者需要做两件事件

  1. 实现接口定义者定义的接口
  2. 创建文件夹META-INF/services,其中放入以该接口的全类名为文件名的文件,文件第一行写入你的实现类的全类名

https://www.chengpei.top/upload/spi_demo.png
这时再调用printLog就会调用实现类实现需要的效果了
看看ServiceLoader的源码其实就会发现,他内部其实是读取了这个文件夹META-INF/services/下的文件内容,利用反射机制创建了接口的实现类对象。
以上演示代码提交到了github,https://github.com/chengpei/spi-demo

实际应用场景介绍

这里以hutool库中的模版引擎封装类TemplateUtil为例,介绍在实际应用场景中,他是如何利用SPI机制实现多种模版引擎的兼容。
通过一行代码可以创建一个模版引擎

TemplateEngine templateEngine = TemplateUtil.createEngine(new TemplateConfig());

但是模版引擎是有很多实现类的,如图:
https://www.chengpei.top/upload/spi_hutool.png
他是怎么确定要使用哪个实现类呢?这里其实如果我们在项目里添加了Freemarker的依赖的话,那么这里创建的就是FreemarkerEngine,如果项目中添加了Beetl的依赖,那些这里创建的就是BeetlEngine实现类,这里就是使用了SPI机制。
看源码这里是创建的文件,并且把所有的实现类都放进去了
https://www.chengpei.top/upload/spi_hutool_template.png
根据源码最后创建引擎实现类时调用了这个方法

public static <T> T loadFirstAvailable(Class<T> clazz) {
	final Iterator<T> iterator = load(clazz).iterator();
	while (iterator.hasNext()) {
		try {
			return iterator.next();
		} catch (ServiceConfigurationError ignore) {
			// ignore
		}
	}
	return null;
}

传进来的clazz当然是TemplateEngine.class接口,这里的load实际上就是调用的ServiceLoader.load(),这里循环会依次获取以上所有文件里的实现类,但是如果项目里没有beetl相关的依赖这里是会报错的,所以这里也捕获了异常什么也不做,继续找下一个实现类加载,因为的项目中有freemarker依赖所以加载FreemarkerEngine实现类没有报错,这里就直接返回不再继续遍历了,所以这个方法就是加载第一个可用的实现类
以上就是hutool库中,SPI机制在模版引擎工具类中的应用了,通过演示代码实操了解原理,再结合其他开源项目中的应用,可以更好的帮助我们理解及运用。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据分析-55-时间序列分析之获取时间序列的自然周期时间区间

文章目录 1 获取某年的总天数1.1 get_year_days()1.2 应用函数2 获取某年的总周数2.1 get_year_weeks()2.2 应用函数3 获取某日期属于某年的周数3.1 get_time_yearweek()3.2 应用函数4 获取某年某周的开始时间和结束时间4.1 get_week_start_end()4.2 应用函数5 获取往前num周期…

基于Spring Boot的房屋租赁系统源码(java+vue+mysql+文档)

项目简介 房屋租赁系统实现了以下功能&#xff1a; 基于Spring Boot的房屋租赁系统的主要使用者管理员可登录系统后台&#xff0c;登录后可对系统进行全面管理&#xff0c;包括个人中心、公告信息管理、租客管理、户主管理、房屋信息管理、看房申请管理、租赁合同管理、收租信…

MySQL--2.1MySQL的六种日志文件

大家好&#xff0c;我们来说一下MySQL的6中日志文件。 1.查询日志 查询日志主要记录mysql的select查询的&#xff0c;改配置是默认关闭的。不推荐开启&#xff0c;因为会导致大量查询日志文件储存占用你的空间。 举例查询一下 select * from class&#xff1b; 开启查询日志的命…

汽车供应链关键节点:物流采购成本管理全解析

在汽车行业&#xff0c;供应链管理是一项至关重要的任务。汽车制造从零部件的生产到整车的交付&#xff0c;涉及多个环节&#xff0c;其中物流、采购与成本管理是核心节点。本文将深入分析这些关键环节&#xff0c;探讨如何通过供应商管理系统及相关工具优化供应链管理。 一、…

Phidata源码分析

https://www.phidata.app/是一家agent saas公司&#xff0c;他们开源了phidata框架&#xff0c;从github介绍上看(https://github.com/phidatahq/phidata)&#xff0c;功能很齐全&#xff0c;我们来学习一下。 首先&#xff0c;明确目的&#xff0c;我想了解下面的实现方式&…

TypeScript Jest 单元测试 搭建

NPM TypeScript 项目搭建 创建目录 mkdir mockprojectcd mockproject初始化NPM项目 npm init -y安装TypeScript npm i -D typescript使用VSCode 打开项目 创建TS配置文件tsconfig.json {"compilerOptions": {"target": "es5","module&…

FPGA技术的深度理解

目录 引言 FPGA的基本原理 结构组成 工作原理 FPGA的设计流程 设计阶段 编程阶段 实现阶段 FPGA的应用领域 FPGA编程技巧和示例代码 编程技巧 示例代码 结论 引言 FPGA&#xff08;现场可编程门阵列&#xff09;是一种可编程的集成电路&#xff0c;它允许用户根据…

Mysql进阶篇

一&#xff1a;存储引擎 二&#xff1a;索引 2.1 索引概述 索引&#xff08;index&#xff09;帮助mysql高效获取数据的数据结构&#xff08;有序&#xff09;。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&…

《Spring Framework实战》15:4.1.4.6.方法注入

欢迎观看《Spring Framework实战》视频教程 方法注入 在大多数应用场景中&#xff0c;容器中的大多数bean都是单例&#xff08;singletons&#xff09;的。当单例bean需要与另一个单例bean协作或非单例bean需与另一非单例bean协作时&#xff0c;通常通过将一个bean定义为另一个…

Flutter:使用FVM安装多个Flutter SDK 版本和使用教程

一、FVM简介 FVM全称&#xff1a;Flutter Version Management FVM通过引用每个项目使用的Flutter SDK版本来帮助实现一致的应用程序构建。它还允许您安装多个Flutter版本&#xff0c;以快速验证和测试您的应用程序即将发布的Flutter版本&#xff0c;而无需每次等待Flutter安装。…

目标客户营销(ABM)结合开源AI智能名片2+1链动模式S2B2C商城小程序的策略与实践

摘要&#xff1a;在数字化营销日益盛行的今天&#xff0c;目标客户营销&#xff08;Account Based Marketing, ABM&#xff09;作为一种高度定制化的营销策略&#xff0c;正逐步成为企业获取高质量客户、提升市场竞争力的重要手段。与此同时&#xff0c;开源AI智能名片21链动模…

docker(目录挂载、卷映射)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、目录挂载1.命令2.案例3.补充 二、卷映射1.命令2.案例 总结 前言 在使用docker部署时&#xff0c;我们如果要改变一些配置项目&#xff0c;不可能每次都进入…

opencv warpAffine仿射变换C++源码分析

基于opencv 3.1.0源代码 sources\modules\imgproc\src\imgwarp.cpp void cv::warpAffine( InputArray _src, OutputArray _dst,InputArray _M0, Size dsize,int flags, int borderType, const Scalar& borderValue ) {...if( !(flags & WARP_INVERSE_MAP) ){//变换矩阵…

使用 IntelliJ IDEA 创建简单的 Java Web 项目

以下是使用 IntelliJ IDEA 创建几个简单的 Java Web 项目的步骤&#xff0c;每个项目实现基本的登录、注册和查看列表功能&#xff0c;依赖 Servlet/JSP 和基本的 Java Web 开发。 前置准备 确保安装了 IntelliJ IDEA Ultimate&#xff08;社区版不支持 Web 应用&#xff09;。…

R语言在森林生态研究中的魔法:结构、功能与稳定性分析——发现数据背后的生态故事!

森林生态系统结构、功能与稳定性分析与可视化研究具有多方面的重要意义&#xff0c;具体如下&#xff1a; 一、理论意义 ●深化生态学理论 通过研究森林生态系统的结构、功能与稳定性&#xff0c;可以深化对生态系统基本理论的理解。例如&#xff0c;生物多样性与生态系统稳定性…

QML states和transitions的使用

一、介绍 1、states Qml states是指在Qml中定义的一组状态&#xff08;States&#xff09;&#xff0c;用于管理UI元素的状态转换和属性变化。每个状态都包含一组属性值的集合&#xff0c;并且可以在不同的状态间进行切换。 通过定义不同的状态&#xff0c;可以在不同的应用场…

Git:Cherry-Pick 的使用场景及使用流程

前面我们说了 Git合并、解决冲突、强行回退等解决方案 >> 点击查看 这里再说一下 Cherry-Pick功能&#xff0c;Cherry-Pick不是merge&#xff0c;只是把部分功能代码Cherry-Pick到远程的目标分支 git cherry-pick功能简介&#xff1a; git cherry-pick 是用来从一个分…

【SpringAOP】Spring AOP 底层逻辑:切点表达式与原理简明阐述

前言 &#x1f31f;&#x1f31f;本期讲解关于spring aop的切面表达式和自身实现原理介绍~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &am…

python基础和redis

1. Map函数 2. filter函数 numbers generate_numbers() filtered_numbers filter(lambda x: x % 2 0, numbers) for _ in range(5):print(next(filtered_numbers)) # 输出: 0 2 4 6 83. filter map 和 reduce 4. picking and unpicking 5. python 没有函数的重载&#xff0…

python-42-使用selenium-wire爬取微信公众号下的所有文章列表

文章目录 1 seleniumwire1.1 selenium-wire简介1.2 获取请求和响应信息2 操作2.1 自动获取token和cookie和agent2.3 获取所有清单3 异常解决3.1 请求url失败的问题3.2 访问链接不安全的问题4 参考附录1 seleniumwire Selenium WebDriver本身并不直接提供获取HTTP请求头(header…