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

Java SPI vs Spring SPI

一、前言:到底什么是SPI?

SPI全称:Service Provider Interface,服务提供者接口
核心思想就8个字:接口与实现完全解耦。
不用修改框架原有源码,只需要提前写好接口,后续新增功能实现类、改个配置文件,框架就能自动发现并加载新功能,这就是SPI的核心价值。
主流技术栈底层全都在用SPI:

  • JDBC驱动加载
  • Dubbo分布式扩展组件
  • SpringBoot全自动装配机制
  • 各类中间件自定义插件拓展

目前行业主流就两套SPI方案:Java原生SPI + Spring升级版SPI,下面从零拆解,全程无晦涩难懂的废话。

二、Java原生SPI:JDK自带,简陋但必学

1、核心强制规则

Java SPI是JDK内置原生能力,不用额外导依赖,但有三条死规则,写错直接失效:

  1. 固定文件夹:必须放在 resources/META-INF/services/ 目录下,目录不能改名、不能换位置
  2. 文件名硬性要求:文件名必须和接口完整全类名一模一样,多一个点、少一个字母都找不到
  3. 文件内容规范:里面填写当前接口所有实现类的完整全类名,一行写一个,换行分隔

2、完整实战代码演示

第一步:定义通用业务接口

// 统一支付渠道标准拓展接口
public interface PayChannelSPI {
// 通用支付下单核心方法
void unifiedPay();
}

第二步:编写多个业务实现类

// 实现类1:微信支付渠道专属实现
public class WechatPayImpl implements PayChannelSPI {
@Override
public void unifiedPay() {
System.out.println("🧧 调用微信支付SDK,发起微信快捷下单支付");
}
}

// 实现类2:支付宝支付渠道专属实现
public class AliPayImpl implements PayChannelSPI {
@Override
public void unifiedPay() {
System.out.println("💳 调用支付宝开放平台,发起支付宝当面付扣款");
}
}

第三步:创建核心SPI配置文件(关键步骤)

在项目资源目录下,严格创建对应路径及文件:
resources/META-INF/services/com.pay.spi.PayChannelSPI
⚠️ 重点:文件名 = 接口全类名,绝对不能乱写!
文件内部写入两个实现类的全类名:

com.pay.spi.WechatPayImpl
com.pay.spi.AliPayImpl

第四步:编写测试类,运行原生SPI加载逻辑

import java.util.ServiceLoader;

public class JavaPaySPITest {
public static void main(String[] args) {
// JDK原生API,绑定支付渠道拓展接口
ServiceLoader<PayChannelSPI> serviceLoader = ServiceLoader.load(PayChannelSPI.class);

// 自动加载所有支付实现,批量执行下单逻辑
for (PayChannelSPI payChannelSPI : serviceLoader) {
payChannelSPI.unifiedPay();
}
}
}

运行结果

🧧 调用微信支付SDK,发起微信快捷下单支付
💳 调用支付宝开放平台,发起支付宝当面付扣款

3、Java SPI底层怎么精准找到对应文件?(解答核心疑问)

很多人疑惑:有几十个接口、几十个配置文件,它会不会找乱、会不会全加载?
答案:绝对不会乱,也不会加载无关文件!
底层执行逻辑全自动精准匹配:

  1. 你代码里传入 VideoPlaySPI.class
  2. 底层自动提取该接口的完整全类名
  3. 自动拼接固定前缀路径:META-INF/services/ + 接口全类名
  4. 只精准读取这一个同名文件,其他所有接口的SPI文件,一概不扫描、不加载、不读取

4、Java原生SPI致命缺点

原生SPI能用,但企业开发没人直接用,全是硬伤:

  • ❌ 不能按需挑选实现类:配置文件里写了几个实现类,就必须全部加载、全部实例化,想单独只用某一个实现,完全做不到
  • ❌ 无法自定义执行顺序:只能按文件里书写顺序加载,不能手动调整优先级
  • ❌ 配置文件极度分散混乱:一个接口对应一个独立配置文件,接口多了之后,services目录下全是零散文件,后期维护极其麻烦
  • ❌ 加载性能差:采用懒加载迭代器遍历,频繁反射创建对象,没有缓存机制

三、Spring SPI:针对性优化,企业主流方案

Spring没有推翻Java原生SPI的核心思想,只是全套升级优化,完美解决原生SPI所有痛点,SpringBoot自动配置底层全靠它支撑。

1、Spring SPI核心优化亮点

  • ✅ 统一配置文件:所有接口、所有实现类,全部写在同一个 spring.factories 文件里,整洁好维护
  • ✅ 支持按需指定实现:想加载哪个就加载哪个,不用全部强制加载
  • ✅ 支持自定义排序优先级:配合注解就能调整实现类执行先后顺序
  • ✅ 自带缓存机制:加载过的实现类直接缓存,提升底层运行性能

2、Spring SPI完整实战案例

第一步:自定义数据库拓展接口(通用标准)

// 全平台消息推送统一拓展接口
public interface MessagePushSPI {
// 统一消息批量推送核心方法
void sendPushMessage();
}

第二步:编写多数据库场景实现类

// 实现1:APP站内弹窗消息推送实现
public class AppPushImpl implements MessagePushSPI {
@Override
public void sendPushMessage() {
System.out.println("📱 成功推送APP弹窗通知,全员触达在线用户");
}
}

// 实现2:短信验证码营销消息推送实现
public class SmsPushImpl implements MessagePushSPI {
@Override
public void sendPushMessage() {
System.out.println("📨 成功调用短信网关,批量下发业务通知短信");
}
}

第三步:创建Spring专属配置文件

路径同样放在:resources/META-INF/ 目录下
新建固定文件名:spring.factories(全局就这一个配置文件)
文件内标准key-value格式编写:

# key = 消息推送接口完整全类名
# value = 多个推送渠道实现类,逗号隔开
com.message.spi.MessagePushSPI=com.message.spi.AppPushImpl,com.message.spi.SmsPushImpl

# ✅ 补充重点:项目有【多个接口、多组实现类】时,统一往下续写即可
# 核心规则:不同接口换行分开写,key单独绑定对应自己的实现类,互不干扰
# 额外示例:新增支付渠道接口、文件上传接口,全部写在这同一个文件里
# com.pay.spi.PayChannelSPI=com.pay.spi.WechatPayImpl,com.pay.spi.AliPayImpl
# com.upload.spi.FileUploadSPI=com.upload.spi.OssUploadImpl,com.upload.spi.LocalUploadImpl

第四步:Spring工具类直接加载测试

import org.springframework.core.io.support.SpringFactoriesLoader;
import java.util.List;

public class SpringMessageSPITest {
public static void main(String[] args) {
// Spring专用加载工具,加载所有消息推送实现类
List<MessagePushSPI> spiList = SpringFactoriesLoader.loadFactories(
MessagePushSPI.class,
Thread.currentThread().getContextClassLoader()
);
// 遍历执行全渠道消息推送
for (MessagePushSPI pushSPI : spiList) {
pushSPI.sendPushMessage();
}
}
}

运行效果

📱 成功推送APP弹窗通知,全员触达在线用户
📨 成功调用短信网关,批量下发业务通知短信

四、核心重点对比(面试直接背,满分答案)

对比维度Java 原生 SPISpring SPI(spring.factories)
配置文件数量一个接口对应一个独立文件,零散混乱全局统一仅一个 spring.factories 文件,整洁易维护
加载方式只能一次性加载全部实现类,无法按需挑选支持按需加载单个实现,也可批量加载,灵活度高
执行顺序固定按文件书写顺序,无法手动调整排序支持 @Order 注解自定义优先级,灵活控制执行顺序
底层工具类JDK原生 ServiceLoaderSpring专属 SpringFactoriesLoader
性能体验无缓存,频繁反射,性能较差自带本地缓存机制,反复加载性能更强
适用场景简单小型工具项目,极少单独使用SpringBoot、中间件、企业级核心框架必备

五、底层核心原理一句话总结

1. Java SPI:靠「固定文件夹+文件名=接口全类名」精准匹配,强制全量加载,简单但短板明显,适合入门学习
2. Spring SPI:沿用SPI解耦核心思想,统一配置、优化加载逻辑,新增排序+缓存+按需加载能力,是SpringBoot自动配置的底层基石

六、总结

SPI是服务提供发现机制,核心实现接口与实现类解耦,提升框架扩展性。Java原生SPI配置分散、只能全量加载、无法排序;Spring SPI优化为统一spring.factories配置文件,支持按需加载、自定义排序、自带缓存,性能更强,SpringBoot自动装配底层依赖Spring SPI实现。

http://www.jsqmd.com/news/769425/

相关文章:

  • 小升初的信息
  • 目录文件管理(mkdir、ls、tree、alias、rm)
  • 抖音下载器终极指南:告别手动操作,实现批量下载自动化
  • 月球基底建造 第三卷第一章 环月空间站奠基,深空全域值守体系与轨道文明中枢成型
  • NVIDIA Profile Inspector终极指南:免费解锁200+隐藏显卡设置,彻底解决游戏卡顿问题
  • 从NGSIM到自动驾驶仿真:如何用sEMA滤波为你的车辆模型提供‘干净’的轨迹真值?
  • PINN调参避坑指南:从N-S方程反演案例看TensorFlow 2.0梯度计算与模型收敛技巧
  • π0.7深度解析:为什么它不是“更大的机器人模型”,而是机器人基础模型的一次方法论转向
  • 23_ble_advertising
  • 免费开源风扇控制软件FanControl终极配置指南:从零到精通
  • Kikusui菊水 149-10A数字高压表 10KV高压测试仪
  • 终极Windows老游戏兼容性修复指南:DDrawCompat完全解析
  • 别再死记硬背了!用‘能量泵’和‘光子跑步机’的比喻,5分钟搞懂激光器三大件
  • RS485电路设计偷懒神器:实测自动方向芯片THVD1426搭建设备级联,5分钟讲清接线要点
  • 解锁数据洞察:如何破解电视价值低估与线上效果误判的困局?
  • 基于MCP协议的企业深度研究工具:3分钟自动化生成全景风险报告
  • RAFT光流估计如何进化成DROID-SLAM?深入拆解其核心更新算子与DBA层
  • 告别卡顿!用STM32F1的DMA驱动ST7735屏幕,让你的UI动画丝滑起来(HAL库实战)
  • CFA备考刷题不踩坑!揽星CFA APP免费题库,适配全阶段、零成本提分 - 速递信息
  • 构建毫秒级延迟的实时AI语音转换系统:基于检索机制的VITS架构深度实践
  • 具身机器人日租金降至3000元,租赁泡沫下产业如何破局?
  • 别再踩坑了!Vue3 + Vite项目里动态图片引入的3种正确姿势(含背景图)
  • 2026年05月03日最热门的开源项目(Github)
  • 【小白也能行】树莓派智能蓝牙音箱项目实践2.0
  • 美团面试官问:BM25和向量怎么选?
  • 45.HASH 函数深度解析
  • 通过用量看板与成本分析优化Taotoken大模型调用开销
  • 城通网盘直连解析终极指南:3步获取高速下载链接的完整方案
  • 程序员想接单?先加入这个圈子再说
  • c++如何实现简单的文件差异比对并生成Patch补丁文件【详解】