🔍 什么是 Java SPI?
Java 的 SPI(Service Provider Interface) 是一种服务发现机制,用于在运行时自动发现接口的实现类,无需硬编码地指定具体的实现。通常用于实现 插件式架构、模块化扩展、解耦依赖。
🌰 举个生活中的类比:
假设你在开一家 “烤肉自助餐厅”
你想让不同的 “烤肉师傅” 来帮你烤肉。
但是你不想关心具体是谁来了,你也希望提前约定:只要你叫一声“来帮忙烤肉的人”,他们自动就出现了,并且能按规矩干活。
那怎么做呢?你贴一个告示:
“来申请工作的烤肉师傅,都按这个模板写申请表,就知道你是谁了。”
这个申请表就是:你定的一个接口(比如叫 BarbecueCooker) ,然后:
- 小王写了个 “川式烤肉” 的实现类;
- 阿毛写了个 “韩式烤肉” 的实现类;
- 小芳写了个 “新疆烤肉” 的实现类。
只要他们都按照这个接口实现了方法,并在自己的骨头盒里贴上告示(配置文件),你就自动认识他们了。
你不需要硬编码说“今天让阿毛来”,而是说:"你们这些注册过的师傅,谁在我这里能干活,都出来吧。"
🎯 那么,什么是 SPI?
你可以把它理解为:
“Java 的插件机制”:一个服务接口(比如支付接口),可以有多个服务提供者(比如支付宝、微信、银联)在不同时候自动注册进来,Java 会帮你自动扫描、加载、实例化这些实现类,只需要你提前约定一个接口。
🔧 SPI 的基本使用步骤
以数据库驱动为例:java.sql.Driver 是接口,各个数据库厂商提供不同的实现(MySQL、PostgreSQL 等),通过 SPI 被自动加载。
✅ 步骤如下:
第一步:定义接口(抽象模块,像个招聘启事)
public interface PaymentService {
void pay(double amount);
}
你可以理解为这是你的招工标准,谁按照我这个接口写类,谁就能干这里的活。
第二步:编写多个实现类(扩展模块,各个烤肉师傅写简历)
public class AlipayService implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("支付宝支付:" + amount);
}
}
public class WechatPayService implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("微信支付:" + amount);
}
}
第三步:创建配置文件(贴告示,说“谁是能干这个的”)
在 resources/META-INF/services/ 目录下新建一个文件,文件名是接口的全限定名:
src/main/resources/META-INF/services/com.example.PaymentService
文件内容为多个实现类的全限定类名:
com.example.AlipayService
com.example.WechatPayService
你可以理解为这就是你的注册表,谁有能力就能来注册这本花名册上。
第四步:使用 ServiceLoader 加载实现类
public class Test {
public static void main(String[] args) {
ServiceLoader<PaymentService> load = ServiceLoader.load(PaymentService.class);
for (PaymentService paymentService : load) {
paymentService.pay(100);
}
}
}
运行结果会是:
支付宝支付:100.0
微信支付:100.0
Java 帮你自动加载了所有在配置文件里注册的实现类。
📚 完整代码示例
项目结构如下:
src/
└── main/
├── java/
│ └── com/
│ └── example/
│ ├── Test.java
│ ├── PaymentService.java
│ ├── AlipayService.java
│ └── WechatPayService.java
└── resources/
└── META-INF/
└── services/
└── com.example.PaymentService
文件内容:
# META-INF/services/com.example.PaymentService
com.example.AlipayService
com.example.WechatPayService
💬 与 Spring IOC / Spring Boot 自动装配的区别:
| 特性 | Java SPI | Spring IOC |
|---|---|---|
| 类加载机制 | JVM 原生 | Spring 容器 |
| 配置方式 | META-INF/services 文件 | XML 或 注解(@Component) |
| 是否支持依赖注入 | ❌ 不支持 | ✅ 强大的依赖管理 |
| 扩展方式 | 需要显示配置 .class | 通过 spring.factories |
🔗 总结
- SPI 是 Java 的一种“找帮手”方法,controller 贴接口,帮工贴上自己的类名,Java 会按图索骥自动找到。
- 📄 告示文件放在:
src/main/resources/META-INF/services/接口的完整类名 - ✅ 用在解耦、扩展、插件系统中非常有用。
- ⚠️ 不建议用于依赖复杂的业务逻辑加载。
- 📌 与 Spring 的自动装配机制不同,它更原始也更直接。
- ⚖️ 实现类只需要提供类名(不写参数、构造器不能复杂),不需要在代码里 import 或 new。
- 👥 谁想加入你系统,只需提供 jar 包,并在 jar 包里写个告示,你就能识别出来。