点一下关注吧!!!非常感谢!!持续更新!!!
🚀 AI篇持续更新中!(长期更新)
AI炼丹日志-30-新发布【1T 万亿】参数量大模型!Kimi‑K2开源大模型解读与实践,持续打造实用AI工具指南!📐🤖
💻 Java篇正式开启!(300篇)
目前2025年07月16日更新到:
Java-74 深入浅出 RPC Dubbo Admin可视化管理 安装使用 源码编译、Docker启动
MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!
📊 大数据板块已完成多项干货更新(300篇):
包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解
Java SPI(Service Provider Interface)是一种内置的服务发现机制,结合接口编程、策略模式和配置文件,允许在运行时动态加载实现类。SPI广泛用于 JDBC、JNDI、日志、XML 解析等场景,核心依赖 ServiceLoader 实现解耦与可插拔扩展。使用时需在 META-INF/services 下配置实现类路径。Dubbo 基于 SPI 机制实现了自己的扩展体系,支持负载均衡、协议选择等插件化设计,用户可自定义实现并通过注解 @SPI 与配置文件指定默认服务,实现灵活的服务扩展与适配。
SPI (Service Provider Interface)
SPI 简介
SPI(Service Provider Interface)是 Java 开发工具包(JDK)内置的一种服务提供发现机制。它作为一种标准化的服务扩展点发现方式,目前被广泛应用于各种框架中来实现服务的动态扩展。
SPI 工作机制
SPI 的核心思想是"面向接口编程+策略模式+配置文件"的组合实现。具体工作流程为:
- 定义服务接口(如
java.sql.Driver
) - 在
META-INF/services/
目录下创建以接口全限定名命名的文件 - 在该文件中写入具体实现类的全限定名(如
com.mysql.jdbc.Driver
) - 通过
ServiceLoader
类加载并实例化这些实现类
SPI 的优势
- 解耦性:将服务接口与具体实现分离,调用方只需面向接口编程
- 扩展性:新增服务实现无需修改原有代码,只需添加新的实现类
- 动态性:服务实现可以在运行时动态发现和加载
- 标准化:作为 JDK 标准机制,提供统一的扩展方式
典型应用场景
- JDBC 数据库驱动加载(
java.sql.Driver
) - JNDI 服务提供者(
javax.naming.spi.InitialContextFactory
) - XML 解析器(
javax.xml.parsers.DocumentBuilderFactory
) - 日志框架(
java.util.logging.LogManager
) - Spring Boot 自动配置机制
示例:JDBC 通过 SPI 加载不同数据库驱动
// 通过 SPI 自动发现并加载驱动
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(url, user, password);
SPI 机制实现了"约定优于配置"的原则,是 Java 生态中实现可插拔架构的重要基础。
JDK中的SPI
Java SPI 使用详解
1. 标准服务接口定义
首先需要定义一个标准服务接口,作为SPI机制的契约。这个接口将被服务提供者实现,被服务调用者使用。
示例:
// 定义支付接口标准
package com.example.spi;
public interface PaymentService {
/**
* 支付方法
* @param amount 支付金额
* @return 支付结果
*/
String pay(double amount);
/**
* 获取支付方式名称
* @return 支付方式
*/
String getPaymentMethod();
}
2. 服务提供者实现
2.1 实现接口
服务提供者需要提供该接口的具体实现。例如我们实现一个支付宝支付服务:
package com.example.provider;
import com.example.spi.PaymentService;
public class AlipayService implements PaymentService {
@Override
public String pay(double amount) {
return "支付宝支付成功,金额:" + amount;
}
@Override
public String getPaymentMethod() {
return "Alipay";
}
}
2.2 创建配置文件
在资源目录下创建 META-INF/services
目录,并添加以接口全限定名命名的文件:
文件路径:META-INF/services/com.example.spi.PaymentService
文件内容:
com.example.provider.AlipayService
2.3 打包注意事项
- 实现类必须包含无参构造器
- 实现类所在的JAR包需要包含META-INF/services目录
- 多个实现可以在配置文件中逐行列出
3. 服务调用者使用
3.1 通过ServiceLoader加载
调用者使用ServiceLoader动态加载实现:
import com.example.spi.PaymentService;
import java.util.ServiceLoader;
public class PaymentApp {
public static void main(String[] args) {
ServiceLoader<PaymentService> services = ServiceLoader.load(PaymentService.class);
for (PaymentService service : services) {
System.out.println("发现支付服务: " + service.getPaymentMethod());
System.out.println(service.pay(100.0));
}
}
}
3.2 类路径配置
确保:
- 接口定义的JAR包在classpath中
- 所有实现类的JAR包也在classpath中
- 如果使用Maven/Gradle,添加相应的依赖关系
4. 高级用法
4.1 多个实现共存
可以同时提供多个实现,例如再添加一个微信支付:
package com.example.provider;
import com.example.spi.PaymentService;
public class WechatPayService implements PaymentService {
@Override
public String pay(double amount) {
return "微信支付成功,金额:" + amount;
}
@Override
public String getPaymentMethod() {
return "WeChat Pay";
}
}
然后在配置文件中添加:
com.example.provider.AlipayService
com.example.provider.WechatPayService
4.2 实际应用场景
SPI机制常用于:
- 支付网关集成
- 数据库驱动加载(如JDBC)
- 日志框架适配
- 序列化协议支持
- 插件化系统开发
5. 注意事项
- ServiceLoader不是线程安全的
- 实现类必须有无参构造器
- 服务加载是延迟初始化的
- 每次调用ServiceLoader.load()都会返回新的实例
- 可以通过迭代器模式获取所有实现
Dubbo中的SPI
dubbo 中大量的使用了SPI来作为扩展点,通过实现同一接口的前提下,可以进行定制自己的实现类,比如比较常见的协议,负载均衡,都可以通过 SPI 的方式进行定制化,自己扩展。Dubbo 中已经存在的所有已经实现的扩展点。
下图中是默认的提供的负载均衡的策略:
扩展点使用
我们将使用三个项目来演示 Dubbo 中扩展点的使用方式,一个主项目 main,一个服务接口项目 api,一个服务实现项目 impl
API 项目创建
● 导入坐标 dubbo
● 创建接口
● 在接口上使用 SPI
impl 项目创建
● 导入 api 项目的依赖
● 建立实现类,为了表达支持多个实现的目的,这里分别创建两个实现,分别是:WzkHumanHelloService 和 WzkDogHelloService。
● SPI 进行声明操作,在 resources 目录下创建 META-INF/dubbo 目录,在目录下创建 “icu.wzk.service.WzkHelloService” 的文件
该文件内容中写入进行扩展的类:
human=icu.wzk.service.impl.WzkHumanHelloService
dog=icu.wzk.service.impl.WzkDogHelloService
我们可以看到下面的目录如下:
这里我们需要回顾一下之前的内容,之前我们已经定义了 WzkHelloService,我们在之前代码的基础上,进行了如下的扩展,这里值得注意的是:默认实现 这里选的是 dog, 也就是:icu.wzk.service.impl.WzkDogHelloService。
PS:SPI指定一个默认实现,属于一个兜底机制,比如消费者调用的时候,没有指定服务,那就会走这个默认的服务。
package icu.wzk.service;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
@SPI("dog")
public interface WzkHelloService {
String sayHello(String name);
@Adaptive
String sayHello( URL url);
}
接着我们要在目录下新建这两个类出来:
WzkHumanHelloService的内容如下所示:
package icu.wzk.service.impl;
import icu.wzk.service.WzkHelloService;
import org.apache.dubbo.common.URL;
public class WzkHumanHelloService implements WzkHelloService {
@Override
public String sayHello(String name) {
return "WzkHuman: " + name;
}
@Override
public String sayHello(URL url) {
return "WzkHuman URL: " + url;
}
}
我们也实现一下 WzkDogHelloService,实现的内容如下:
package icu.wzk.service.impl;
import icu.wzk.service.WzkHelloService;
import org.apache.dubbo.common.URL;
public class WzkDogHelloService implements WzkHelloService {
@Override
public String sayHello(String name) {
return "WzkDog: " + name;
}
@Override
public String sayHello(URL url) {
return "WzkDog URL: " + url;
}
}