深入理解 Java SPI - 概念、原理、应用
零、前言
在当今互联网时代,应用程序越来越复杂,对于我们开发人员来说,如何实现高效的组件化和模块化已经成为了一个重要的问题。而 Java SPI(Service Provider Interface)机制,作为一种基于接口的服务发现机制,可以帮助我们更好地解决这个问题。这样会程序具有高度的灵活性、解耦、可扩展性!
一、概念
SPI 机制,全称** Service Provider Interface**,即服务提供者接口,是从Java 6 开始引入的,一种基于 ClassLoader 来发现并加载服务的机制,是Java提供的一套用来被第三方实现或者扩展的API,可以用来启用框架扩展和替换组件,它的核心类是 java.util.ServiceLoader。
单从字面上看,可以这样理解,该机制提供了一种可根据接口类型去动态加载出接口实现类对象的功能。打一个比喻,该机制就类似Spring容器,通过IOC将对象的创建交给Spring容器处理,若需要获取某个类的对象,就从Spring容器里取出使用即可。同理,在SPI机制当中,提供了一个类似Spring容器的角色,叫**【服务提供者】**,在代码运行过程中,若要使用到实现了某个接口的服务实现类对象,只需要将对应的接口类型交给服务提供者,服务提供者将会动态加载出所有实现了该接口的服务实现类对象,最后给到服务使用者使用。
在大型系统设计中,开闭原则和解耦是必不可少的,而SPI机制的核心便是解耦合。通过SPI机制,将实现类隐藏在接口后面,根据需要寻找服务实现,SPI就提供了这样的服务发现机制。
Effective Java中也提到SPI是一种将服务接口与服务实现分离以达到解耦、大大提升了程序可扩展性的机制。引入服务提供者就是引入了SPI接口的实现者,通过本地的注册发现获取到具体的实现类,轻松可插拔。
SPI 的核心思想是将服务接口与其具体实现分离,从而提升程序的扩展性和灵活性。这种机制通过配置文件和ServiceLoader来实现。具体来说,SPI机制主要由以下几个部分组成:
- Service:这是被第三方实现或扩展的API,是一个公开的接口或抽象类,定义了一个抽象的功能模块。
- Service Provider Interface:定义了服务接口。
- Service Provider:实现了Service Provider Interface的类。
- ServiceLoader:负责加载并实例化服务提供者的实现,是SPI机制的核心组件。
优缺点
优点:
- 解耦性强:通过将服务接口与其实现分离,提高了系统的灵活性和可维护性。
- 动态加载:可以在运行时动态加载和替换服务实现,增强了系统的可扩展性。
- 标准化配置:通过配置文件的方式进行服务提供者的选择,简化了开发过程。
缺点:
- 性能开销:由于需要加载和实例化多个实现类,可能会带来一定的性能开销。
- 配置较麻烦:SPI需要在META-INF/services目录下创建配置文件,并将实现类的类名写入其中。这使得配置相对较为繁琐。
- 安全性不足:SPI提供者必须将其实现类名称写入到配置文件中,因此如果未正确配置,则可能存在安全风险。
二、应用场景
Java SPI机制是一种服务提供者发现的机制,适用于需要在多个实现中选择一个进行使用的场景。
常见的应用场景包括:
应用名称 | 具体应用场景 |
---|---|
数据库驱动程序加载 | JDBC为了实现可插拔的数据库驱动,在Java.sql.Driver接口中定义了一组标准的API规范,而具体的数据库厂商则需要实现这个接口,以提供自己的数据库驱动程序。在Java中,JDBC驱动程序的加载就是通过SPI机制实现的。 |
日志框架的实现 | 流行的开源日志框架,如Log4j、SLF4J和Logback等,都采用了SPI机制。用户可以根据自己的需求选择合适的日志实现,而不需要修改代码。 |
Spring框架 | Spring框架中的Bean加载机制就使用了SPI思想,通过读取classpath下的META-INF/spring.factories文件来加载各种自定义的Bean。 |
Dubbo框架 | Dubbo框架也使用了SPI思想,通过接口注解@SPI声明扩展点接口,并在classpath下的META-INF/dubbo目录中提供实现类的配置文件,来实现扩展点的动态加载。 |
MyBatis框架 | MyBatis框架中的插件机制也使用了SPI思想,通过在classpath下的META-INF/services目录中存放插件接口的实现类路径,来实现插件的加载和执行。 |
Netty框架 | Netty框架也使用了SPI机制,让用户可以根据自己的需求选择合适的网络协议实现方式。 |
Hadoop框架 | Hadoop框架中的输入输出格式也使用了SPI思想,通过在classpath下的META-INF/services目录中存放输入输出格式接口的实现类路径,来实现输入输出格式的灵活配置和切换。 |
Spring的SPI机制相对于Java原生的SPI机制进行了改造和扩展,主要体现在以下几个方面:
- 支持多个实现类:Spring的SPI机制允许为同一个接口定义多个实现类,而Java原生的SPI机制只支持单个实现类。这使得在应用程序中使用Spring的SPI机制更加灵活和可扩展。
- 支持自动装配:Spring的SPI机制支持自动装配,可以通过将实现类标记为Spring组件(例如@Component),从而实现自动装配和依赖注入。这在一定程度上简化了应用程序中服务提供者的配置和管理。
- 支持动态替换:Spring的SPI机制支持动态替换服务提供者,可以通过修改配置文件或者其他方式来切换服务提供者。而Java原生的SPI机制只能在启动时加载一次服务提供者,并且无法在运行时动态替换。
- 提供了更多扩展点:Spring的SPI机制提供了很多扩展点,例如BeanPostProcessor、BeanFactoryPostProcessor等,可以在服务提供者初始化和创建过程中进行自定义操作。
其他框架也是对Java SPI进行改造和扩展增强,从而更好的提供服务!
三、Java SPI 在JDBC 中的应用
SPI 机制广泛应用于各种框架和库中,例如JDBC、Dubbo等。它使得应用程序可以在不修改代码的情况下替换或扩展功能。例如,在数据库连接池中,可以通过SPI机制选择不同的数据库驱动。
JDBC,全称 Java DataBase Connectivity
- JDBC 即使用java语言来访问数据库的一套API
- 不同数据库厂商都会提供各自的JDBC实现
熟悉的 Class.forName 加载驱动:
使用SPI后,只需要引入对应的依赖 JAR 包即可:
SPI 的规范在 JDBC 中的实践
四、实现方式
Java SPI 的实现依赖于ServiceLoader
类。ServiceLoader
通过使用线程上下文类加载器(Thread Context Class载荷器)来加载SPI接口的实现类。实现类的全路径名需配置在META-INF/services/
目录下的相应文件中,例如META-INF/services/java.sql.Driver
。
具体步骤如下:
- 在
META-INF/services/
目录下创建一个以接口全限定名命名的文件。 - 在该文件中写入实现类的全限定名,每个实现类占一行。
- 使用
ServiceLoader.load ()
方法加载这些实现类,并通过反射机制实例化它们。
五、自定义 SPI 应用实例
项目关系图
simple-isp-mobile
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.tyron</groupId><artifactId>java-spi-example</artifactId><version>{version}</version></parent><artifactId>simple-isp-mobile</artifactId><version>${revision}</version><name>simple-isp-mobile</name><description>中国移动服务商</description><dependencies><dependency><groupId>com.tyron</groupId><artifactId>simple-api</artifactId><version>{version}</version><scope>compile</scope></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build>
</project>
simple-isp-unicom
项目源码:https://gitee.com/tyronchen/spring-boot-learn/tree/master/simple_spi_example
为了充分发挥SPI机制的优势,建议遵循以下最佳实践:
- 合理配置服务提供者:确保每个服务接口只有一个明确的实现,并且该实现能够高效地完成任务。
- 优化加载策略:避免一次性加载所有可能的服务实现,可以采用按需加载的方式减少资源消耗。
- 统一配置格式:保持配置文件的一致性和规范性,便于管理和维护。
参考
Java SPI概念、实现原理、优缺点、应用场景、使用步骤、实战SPI案例
10分钟让你彻底明白Java SPI
Java SPI机制实战详解及源码分析
相关文章:

深入理解 Java SPI - 概念、原理、应用
零、前言 在当今互联网时代,应用程序越来越复杂,对于我们开发人员来说,如何实现高效的组件化和模块化已经成为了一个重要的问题。而 Java SPI(Service Provider Interface)机制,作为一种基于接口的服务发现…...
JavaScript - 判断数组中是否包含某个的元素的几种方式
目录 1. 使用 includes 方法 2. 使用 indexOf 方法 3. 使用 find 方法 4. 使用 some 方法 5. 使用 filter 方法 6. 使用 every 方法 应该算是前端开发过程中比较常用的基本操作,话不多说,看代码。 1. 使…...
如何用AI颠覆企业未来:从大企业到中小型企业的实战攻略
如何用AI颠覆企业未来:从大企业到中小型企业的实战攻略 AI大佬经验分享:聊聊企业定制化AI需求和应用场景 今天想跟大家聊聊我在AI领域的一些经验和见解,希望能对大家有所启发。最近,不少企业都对AI很感兴趣,我也经常…...

Linux磁盘管理_LVM逻辑卷_SWAP交换分区_Centos-LVM格式磁盘扩容
目录 一、基本磁盘管理1.1 创建分区1.2 创建文件系统1.3 挂载mount1.4 查看挂载信息1.5 重启失效解决方式 二、逻辑卷LVM2.1 LVM2.2 创建LVM2.3 扩大卷组VG2.4 命令汇总 三、交换分区SWAP管理3.1 SWAP3.2 查看swap3.3 增加交换分区 四、Centos调整分区,在线调整分区…...

C++ 函数模板和类模板
参考视频:C类模板_哔哩哔哩_bilibili 遗留问题:编译器怎么处理函数模板和类模板 目录 一、为什么会有函数模版?函数模板是为了解决什么问题? 二、函数模板的概念 三、函数模版的使用 四、函数模板的特化 五、类模板的概念 …...

安卓Termux系统设备安装内网穿透工具实现远程使用SFTP传输文件
文章目录 前言1. 安装openSSH2. 安装cpolar3. 远程SFTP连接配置4. 远程SFTP访问4. 配置固定远程连接地址 前言 本教程主要介绍如何在安卓 Termux 系统中使用 SFTP 文件传输,并结合cpolar内网穿透工具生成公网地址,轻松实现无公网IP环境远程传输…...
文件属性获取
1、getpwuid函数 #include <stdio.h> #include <sys/types.h> #include <pwd.h> int main(int argc, char *argv[]) {uid_t uid 1000;struct passwd * pw getpwuid(uid);printf("name:%s gid:%d info:%s wd:%s shell:%s\n",pw->pw_name,pw-&g…...

C:冒泡排序
1、冒泡排序介绍: 冒泡排序的核心思想就是:两两相邻的元素进行比较。 先用一个例子来帮助大家理解一下冒泡排序的算法是怎们进行的 有一排高矮不同的人站成一列,要按照从矮到高的顺序重新排队。 冒泡排序的方法就是,从第一个人…...

探秘C# LINQ元素运算:原理阐释与实践指南
文章目录 一、LINQ元素运算符概述二. ElementAt 和 ElementAtOrDefault三. First 和 FirstOrDefault四. Last 和 LastOrDefault五. Single 和 SingleOrDefault六. Where 和 Select七、实际应用场景示例总结 LINQ(Language-Integrated Query)是C#中强大且…...
根据bean的名称获取bean,静态方法查询数据库
根据bean名称获取bean 1.先创建bean,如template package com.test.game.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate;import…...

剪画小程序:音频剪辑新手入门:基础操作指南!
亲爱的小伙伴们,你是否对音频剪辑充满好奇,却不知道从何下手?今天,就让我们用【剪画】一起揭开音频剪辑基础操作的神秘面纱! 音频拼接——打造个性音乐串烧 音频拼接是将多个音频片段组合在一起,创造出独特…...

IDEA中maven jar下载失败问题处理
前言 对于在IDEA中maven爆红问题,主要分为两类,一个是全部爆红,一个是部分爆红。 全部爆红 这类问题主要是maven配置没有搞好,可以根据下列步骤自查 1.配置好国内的Maven源 第一步:打开IDEA,查看配置 第二步: 检查…...
C++中,函数返回const类型有什么作用,请举例说明
在C中,函数返回const类型的主要作用是确保返回的对象不会被修改。这种保护机制增强了代码的健壮性和可维护性,尤其是在你希望保证函数返回的数据不被意外篡改时。下面通过几个例子来说明函数返回const类型的作用。 例子 1: 返回常量引用 当你从函数中返…...

Html详解——Vue基础
HTML是什么? 超文本标记语言(英语:HyperText Markup Language,简称:HTML)是一种用来结构化 Web 网页及其内容的标记语言。网页内容可以是:一组段落、一个重点信息列表、也可以含有图片和数据表…...

【安规电容知识点总结】
安规电容知识点总结 安规电容简介安规电容的种类X电容和Y电容X电容和Y电容示意图安规电容的型号与应用安规电容简介 安规电容:是指用于这样的场合,即电容器失效后,不会导致电击,不会危及到人身安全。 具体来说,所谓安规电容是一种与普通电容相比符合安全认证的电容,故称…...

R9000P 双系统安装 win11 和 ubuntu
网上了解到一堆关于 r9000p 安装较老的ubuntu系统,会有一堆问题 可能是电脑硬件比较新,较老的系统相关方面不兼容 那么干脆直接装新一点的系统 我安装了 Ubuntu 22.04 1 根据相关教程利用u盘制作系统盘 ultraISO 推荐使用清华源 速度快一点 https://…...

8月8日笔记
8月8日笔记 msf常见命令 启动MSF控制台 msfconsole: 启动MSF控制台。msfconsole -h: 显示帮助菜单。msfconsole -q: 启动MSF控制台并立即退出。 导航和管理 back: 返回上一级菜单。exit: 退出MSF控制台。banner: 显示MSF的横幅。cd: 更改工作目录。color: 开启或关闭彩色输…...

【单片机开发软件】使用VSCode开发STM32环境搭建
💌 所属专栏:【单片机开发软件技巧】 😀 作 者: 于晓超 🚀 个人简介:嵌入式工程师,专注嵌入式领域基础和实战分享 ,欢迎咨询! 💖 欢迎大家࿱…...
第十五届蓝桥杯大赛青少组——赛前解析(算法)
算法:进制转换、模拟算法,枚举算法,冒泡排序,插入排序,选择排序,递推算法,递归算法,贪心算法。 1.进制转换 二进制:只包含0和1 八进制:只包含0-7 十进制&…...

工作助手C#研究笔记(5)
通过示例对C#程序的结构逻辑进行研究梳理,虽然通过阅读相关书籍,但是来的效果更慢。一下相关内容可能有误,请谨慎听取。 TaskToDoList-master 1.XAML “XAML”是WPF中专门用于设计UI的语言,优点是 1.XAML可以设计出专业的UI和…...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...

HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...