Java SPI机制详解-01
1. 概述
SPI(Service Provider Interface),是 Java 6 引入了一个内置功能,实现服务提供发现和加载机制,使之与特定接口的匹配。
SPI 机制的核心思想就是 解耦 ,将装配的控制权移到程序之外,这在企业模块化设计中非常重要。
有的人喜欢拿 SPI 与 API 之间做比较,关于二者之间的差异主要在于侧重点不一样。
API:侧重调用并用于实现目标的类、接口、方法等的描述;SPI:侧重对已实现目标类、接口、方法的扩展和实现;

2. 使用场景
调用者根据实际使用需要,启用、扩展、或者替换框架的实现策略。
-
数据库驱动加载接口实现类的加载:
JDBC加载不同类型数据库的驱动 -
日志门面接口实现类加载:
SLF4J加载不同提供商的日志实现类 -
Spring框架:Spring中大量使用SPI,比如:对Servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等
3. 入门使用介绍
在实际使用 SPI 需要遵循以下约定:
- 按照定义创建加载文件:当服务提供者提供了接口的一种具体实现后,在
jar包的META-INF/services目录下创建一个以接口全限定名为命名的文件,内容为实现类的全限定名; - 动态加载:主程序通过
java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM; - 无参构造方法:
SPI实现类必须携带一个不带参数的构造方法; - 相同classpath:接口实现类所在的
jar包放在主程序的classpath中;
4. 示例
4.1. 构建接口
此处演示作用,只定义一组接口,接口为 灵长类 动物,它包含一个方法, 叫 动作 。
package io.github.rothschil.spi.framework;/*** 灵长类动物* @author <a href="mailto:WCNGS@QQ.COM">Sam</a>* @version 1.0.0*/
public interface Primate {void action();
}
4.2. 拓展实现
灵长类 这个接口,我们假定它的实现为人类 、猴子,它们都能有属于自己的动物。
- 人类
package io.github.rothschil.spi.framework.impl;import io.github.rothschil.spi.framework.Primate;public class Human implements Primate {@Overridepublic void action() {System.out.println("Human");}
}
- 猴子
package io.github.rothschil.spi.framework.impl;import io.github.rothschil.spi.framework.Primate;public class Monkey implements Primate {@Overridepublic void action() {System.out.println("Monkey");}
}
4.3. 构建META-INF下文件
- 文件路径:
resources/META-INF/services - 文件名:
io.github.rothschil.spi.framework.Primate - 文件内容:
io.github.rothschil.spi.framework.impl.Human
io.github.rothschil.spi.framework.impl.Monkey
4.4. 验证
此处用到 ServiceLoader 类加载器,通过 Primate.class 将它的实现以此加载到 JVM 中。
4.5. 结果

package io.github.rothschil.spi.framework;import java.util.ServiceLoader;public class TestSpi {public static void main(String[] args) {ServiceLoader<Primate> primates = ServiceLoader.load(Primate.class);for (Primate pr : primates) {pr.action();}}
}
4.5.1. ServiceLoader


ServiceLoader 本身就是一个迭代器,它的属性并不多,我们以 ServiceLoader.load 为入口,一步一步看下去。
- 调用
ServiceLoader.load根据当前线程调用类记载器ClassLoader利用ServiceLoader构造器,创建一个实例。- 类加载器
- 访问控制器
- 目标类
- 迭代器
ServiceLoader先判断成员变量providers对象中(LinkedHashMap<String,S>类型)是否有缓存实例对象,如果有缓存,直接返回- 读取
META-INF/services/下的配置文件,获得所有能被实例化的类的名称,值得注意的是,ServiceLoader可以跨越jar包获取META-INF下的配置文件 - 通过反射方法Class.forName()加载类对象,并用instance()方法将类实例化
- 把实例化后的类缓存到providers对象中,(LinkedHashMap<String,S>类型)然后返回实例对象
- 读取
public final class ServiceLoader<S>implements Iterable<S>
{private static final String PREFIX = "META-INF/services/";// The class or interface representing the service being loadedprivate final Class<S> service;// The class loader used to locate, load, and instantiate providersprivate final ClassLoader loader;// The access control context taken when the ServiceLoader is createdprivate final AccessControlContext acc;// Cached providers, in instantiation orderprivate LinkedHashMap<String,S> providers = new LinkedHashMap<>();// The current lazy-lookup iteratorprivate LazyIterator lookupIterator;/*** Clear this loader's provider cache so that all providers will be* reloaded.** <p> After invoking this method, subsequent invocations of the {@link* #iterator() iterator} method will lazily look up and instantiate* providers from scratch, just as is done by a newly-created loader.** <p> This method is intended for use in situations in which new providers* can be installed into a running Java virtual machine.*/public void reload() {providers.clear();lookupIterator = new LazyIterator(service, loader);}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();}
5. 总结
这是我们第一篇 SPI 描述,主要引入它的几个概念、用途以及与我们 API 的区别,最后我们通过一个手写的样例,虽然通过 ServiceLoader 加载的,在实际生产环境中,这存在注入线程安全以及不够灵活注入从而导致资源开销大等问题,但是这只为我们 SPI 学习加深理解,算开启正式 SPI 之旅。
相关文章:
Java SPI机制详解-01
1. 概述 SPI(Service Provider Interface),是 Java 6 引入了一个内置功能,实现服务提供发现和加载机制,使之与特定接口的匹配。 SPI 机制的核心思想就是 解耦 ,将装配的控制权移到程序之外,这…...
由浅入深C系列六:C中实现字符串trim的功能
C中实现字符串trim的功能 简介设计思路代码实现运行效果 简介 一个项目中,需要用c语言实现对字符串中的字定字符进行过滤并从字符串的删除,查询了C语言的基本库,没有发现有这样的函数,于是发挥程序员的主观能力性,自力…...
博客网站添加复制转载提醒弹窗Html代码
网站如果是完全禁止右键(复制、另存为等)操作,对用户来说体验感会降低,但是又不希望自己的原创内容直接被copy,今天飞飞和你们分享几行复制转载提醒弹窗Html代码。 效果展示: 复制以下代码,将其…...
ubuntu下nfs服务安装
操作系统:ubuntu22.04.2 一、服务端安装与配置 1、在服务端安装nfs服务端组件 sudo apt install nfs-kernel-server 2、创建共享目录share并且授权所有人可以访问 sudo mkdir /shared sudo chmod -R 777 /shared 3、配置nfs sudo vim /etc/exports 这将允许…...
Unity框架学习--2
接上文 IOC 容器是一个很方便的模块管理工具。 除了可以用来注册和获取模块,IOC 容器一般还会有一个隐藏的功能,即: 注册接口模块 抽象-实现 这种形式注册和获取对象的方式是符合依赖倒置原则的。 依赖倒置原则(Dependence I…...
WebRTC音视频通话-实现GPUImage视频美颜滤镜效果iOS
WebRTC音视频通话-实现GPUImage视频美颜滤镜效果 在WebRTC音视频通话的GPUImage美颜效果图如下 可以看下 之前搭建ossrs服务,可以查看:https://blog.csdn.net/gloryFlow/article/details/132257196 之前实现iOS端调用ossrs音视频通话,可以查…...
82. 删除排序链表中的重复元素 II
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 解题思路:设置一个新的哑元节点result,作为头节点,将head中不重复地节点依次链接到哑元节点后面,最后返回result.next 初始值&…...
centos 7.x 单用户模式
最近碰到 centos 7.9 一些参数设置错误无法启动系统的情况,研究后可以使用单用户模式进入系统进行恢复操作。 进入启动界面,按 e ro 替换为 rw init/sysroot/bin/sh 替换前 替换后 Ctrl-x 进行重启进入单用户模式 执行 chroot /sysroot 可以查看日…...
取证--理论
资料: 各比赛 Writeup : https://meiyacup.cn/Mo_index_gci_36.html 哔站比赛复盘视频: https://space.bilibili.com/453117423?spm_id_from333.337.search-card.all.click 自动分析取证四部曲 新建案例添加设备自动取证制作报告 取证大…...
Tik Tok娱乐+电商MCN怎么做?
在美国外的热门市场中,TikTok 主要做的区域市场包括中东、拉美、欧洲和东亚,而这里面适合做电商的其实并不多。 欧洲、东亚都属于成熟市场,且 TikTok 本身在欧洲面临 DSA 法案更严格的审查,与在英国相同,欧洲各市场消…...
java 自定义xss校验注解实现
自定义一个注解Xss。名字随意 import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Targe…...
Selenium图片滑块验证码
因为种种原因没能实现愿景的目标,在这里记录一下中间结果,也算是一个收场吧。这篇文章主要是用selenium解决滑块验证码的个别案列。 思路: 用selenium打开浏览器指定网站 将残缺块图片和背景图片下载到本地 对比两张图片的相似地方&#…...
CAP理论与MongoDB一致性,可用性的一些思考
正文 大约在五六年前,第一次接触到了当时已经是hot topic的NoSql。不过那个时候学的用的都是mysql,Nosql对于我而言还是新事物,并没有真正使用,只是不明觉厉。但是印象深刻的是这么一张图片(后来google到图片来自这里&…...
lc2536.子矩阵元素加1
暴力解法:直接按照题目所示在矩阵的相应位置加一 时间复杂度:O(n2 * queries.length) 空间复杂度:O(1) 二维差分:创建二维差分数组,通过对差分数组的修改来影响原来的数组,最后还原 时间复杂度&#x…...
C#使用OpenCv(OpenCVSharp)图像全局二值化处理实例
本文实例演示C#语言中如何使用OpenCv(OpenCVSharp)对图像进行全局二值化处理。 目录 图像二值化原理 函数原型 参数说明 实例 效果 图像二值化原理...
Patch SCN一键解决ORA-600 2662故障---惜分飞
客户强制重启库之后,数据库启动报ORA-600 2037,ORA-745 kcbs_reset_pool/kcbzre1等错误 Wed Aug 09 13:25:38 2023 alter database mount exclusive Successful mount of redo thread 1, with mount id 1672229586 Database mounted in Exclusive Mode Lost write protection d…...
const、指针、引用的综合
目录 代码段 定义引用变量的技巧 内存某处 正误判定技巧 温故知新 代码段 定义引用变量的技巧 // 定义引用变量的技巧#include<iostream> using namespace std;int main() {int a 1;int * p &a;// 首先,定义一个指针变量int * * q1 &p;// 然…...
gitee linux免密/SSH 方式连接免登录
目录 账号密码方式免登录(不推荐)添加git配置新建保存密码文件git clone SSH 方式连接免登录(推荐)生成SSH公钥通过 ssh-keygen 程序创建找到SSH公钥 在gitee中添加公钥git clone 参考 账号密码方式免登录(不推荐&…...
计网第一章
注意:计网知识点十分多,在本篇及后续博客主要记录个人认为比较重要的知识点。 1.计算机网络的基本概念 计算机网络就是自治的计算机互连起来的集合。计算机网络可以简称为网络,而互连网就是把许多网络连接起来,即网络的网络。 …...
windows升级记
我的笔记本原来的windows的版本是win10,本来想使用windows 更新下最新的补丁包,但是一直报错,出现错误号:0x80004005,在网上找了一堆的资料都没有办法解决问题,于是把问题反馈到微软的技术服务中心,服务中心…...
FreeRTOS进阶指南:流缓冲区与消息缓冲区的实战应用与性能优化
1. 流缓冲区与消息缓冲区基础解析 第一次接触FreeRTOS的缓冲区功能时,我完全被官方文档绕晕了。直到在真实项目中踩了几个坑才明白,这俩兄弟其实就像快递站的两种取件方式:流缓冲区是自助取件(按重量取),消…...
5分钟拯救珍贵视频!untrunc视频修复工具终极指南
5分钟拯救珍贵视频!untrunc视频修复工具终极指南 【免费下载链接】untrunc Restore a truncated mp4/mov. Improved version of ponchio/untrunc 项目地址: https://gitcode.com/gh_mirrors/un/untrunc 您是否曾因相机突然断电、存储卡故障或传输中断而丢失珍…...
dotnetbook实战解析:10个关键技巧优化.NET应用程序性能
dotnetbook实战解析:10个关键技巧优化.NET应用程序性能 【免费下载链接】dotnetbook .NET Platform Architecture book (English, Chinese, Russian) 项目地址: https://gitcode.com/gh_mirrors/do/dotnetbook 在开发.NET应用程序时,性能优化是提…...
技术合作的模式探索与合作伙伴选择
技术合作的模式探索与合作伙伴选择 在当今快速发展的科技领域,技术合作已成为企业提升创新能力、降低研发成本、加速市场拓展的重要途径。无论是初创企业还是行业巨头,都需要通过合作实现资源共享与优势互补。如何选择合适的合作模式与合作伙伴…...
Vue3后台管理系统开发革命:如何用vue-admin-box实现零门槛企业级应用
Vue3后台管理系统开发革命:如何用vue-admin-box实现零门槛企业级应用 【免费下载链接】vue-admin-box vue3,vite,element-plus中后台管理系统,集成四套基础模板,大量可利用组件,模板页面 项目地址: https://gitcode.com/gh_mirr…...
逆向工程实战:3步打造Windows微信/QQ防撤回终极方案
逆向工程实战:3步打造Windows微信/QQ防撤回终极方案 【免费下载链接】RevokeMsgPatcher :trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁(我已经看到了,撤回也没用了) 项目地址: https://gitcode.com/…...
AI专著生成超实用!工具推荐与使用攻略全解析
撰写学术专著时,研究者们需要在“内容的深度”和“覆盖的广度”之间找到一个合适的平衡点,这常常成为研究者们难以逾越的瓶颈。从深度来看,AI写专著需要具备扎实的学术基础,不能仅仅掀起表面,必须清楚地解释“是什么”…...
Rust 内存分配与所有权管理
Rust 内存分配与所有权管理:安全与性能的完美平衡 在编程语言的世界中,内存管理一直是开发者面临的核心挑战之一。传统语言如 C/C 依赖手动管理内存,容易引发内存泄漏或悬垂指针;而 Java 等语言采用垃圾回收机制(GC&a…...
如何用ncmdump一键解密网易云音乐NCM文件?3步实现音乐自由
如何用ncmdump一键解密网易云音乐NCM文件?3步实现音乐自由 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 您是否遇到过这样的困扰:在网易云音乐下载的歌曲只能在特定客户端播放,想在手机、车载音…...
LLaMA-Factory实战:基于Qwen2.5-VL-7B-Instruct的印章识别微调指南
1. 环境准备与基础配置 在开始微调Qwen2.5-VL-7B-Instruct模型之前,我们需要搭建好开发环境。这里推荐使用Docker容器来保证环境的一致性,避免因为系统差异导致的问题。我实测过在Ubuntu 20.04和22.04系统上都能稳定运行,下面分享具体配置步骤…...
