Java-75 深入浅出 RPC Dubbo Java SPI机制详解:从JDK到Dubbo的插件式扩展

点一下关注吧!!!非常感谢!!持续更新!!!

🚀 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 的核心思想是"面向接口编程+策略模式+配置文件"的组合实现。具体工作流程为:

  1. 定义服务接口(如java.sql.Driver
  2. META-INF/services/目录下创建以接口全限定名命名的文件
  3. 在该文件中写入具体实现类的全限定名(如com.mysql.jdbc.Driver
  4. 通过ServiceLoader类加载并实例化这些实现类

SPI 的优势

  1. 解耦性:将服务接口与具体实现分离,调用方只需面向接口编程
  2. 扩展性:新增服务实现无需修改原有代码,只需添加新的实现类
  3. 动态性:服务实现可以在运行时动态发现和加载
  4. 标准化:作为 JDK 标准机制,提供统一的扩展方式

典型应用场景

  1. JDBC 数据库驱动加载(java.sql.Driver
  2. JNDI 服务提供者(javax.naming.spi.InitialContextFactory
  3. XML 解析器(javax.xml.parsers.DocumentBuilderFactory
  4. 日志框架(java.util.logging.LogManager
  5. 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 {@Overridepublic String pay(double amount) {return "支付宝支付成功,金额:" + amount;}@Overridepublic 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 类路径配置

确保:

  1. 接口定义的JAR包在classpath中
  2. 所有实现类的JAR包也在classpath中
  3. 如果使用Maven/Gradle,添加相应的依赖关系

4. 高级用法

4.1 多个实现共存

可以同时提供多个实现,例如再添加一个微信支付:

package com.example.provider;import com.example.spi.PaymentService;public class WechatPayService implements PaymentService {@Overridepublic String pay(double amount) {return "微信支付成功,金额:" + amount;}@Overridepublic String getPaymentMethod() {return "WeChat Pay";}
}

然后在配置文件中添加:

com.example.provider.AlipayService
com.example.provider.WechatPayService
4.2 实际应用场景

SPI机制常用于:

  • 支付网关集成
  • 数据库驱动加载(如JDBC)
  • 日志框架适配
  • 序列化协议支持
  • 插件化系统开发

5. 注意事项

  1. ServiceLoader不是线程安全的
  2. 实现类必须有无参构造器
  3. 服务加载是延迟初始化的
  4. 每次调用ServiceLoader.load()都会返回新的实例
  5. 可以通过迭代器模式获取所有实现

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);@AdaptiveString 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 {@Overridepublic String sayHello(String name) {return "WzkHuman: " + name;}@Overridepublic 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 {@Overridepublic String sayHello(String name) {return "WzkDog: " + name;}@Overridepublic String sayHello(URL url) {return "WzkDog URL: " + url;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/91662.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/91662.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【橘子分布式】gRPC(编程篇-上)

一、简介 我们之前学习了grpc的一些理论知识&#xff0c;现在我们开始正式进入编程环节。 我们的项目结构和之前的thrift结构还是一样的&#xff0c;一个common,一个client&#xff0c;一个server。只不过在grpc这里common它一般叫做api模块。还是放置一些公共的实体类&#x…

IOS 18下openURL 失效问题

突然有一天有玩家反馈说应用打开外部连接打不开了&#xff0c;于是查了一下&#xff0c;报错&#xff1a;BUG IN CLIENT OF UIKIT: The caller of UIApplication.openURL(_:) needs to migrate to the non-deprecated UIApplication.open(_:options:completionHandler:). Force…

前端面试题(React 与 Vue)

目录 一、React 函数组件 Fiber架构 组件重新渲染 组件通信 为什么不能在if中使用hook useEffect与useLayoutEffect区别 性能优化hooks 受控组件与非受控组件 redux与zustand区别 二、Vue vue2与vue3区别 生命周期 computed与watch区别 v-if与v-show区别 v-mod…

大模型格式

目录 大模型格式&#xff1a; ollma 可以加载gguf ChatGPT 说&#xff1a; &#x1f50d; 什么是 GGUF&#xff1f; 大模型格式&#xff1a; Ollama 模型格式只能运行已打包成 .gguf 格式的模型&#xff0c;或通过其 Modelfile 方式构建 ModelScope 模型格式大多使用 Hug…

数据结构 栈(1)

1. 栈的概念和结构之前几篇我们分别讲解了顺序表和单链表的内容&#xff0c;今天我们又来学习一个新的关于数据结构的内容--- 栈 。栈&#xff1a;栈也属于线性表 , 但它是一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一…

【Android代码】绘本翻页时通过AI识别,自动通过手机/pad朗读绘本

核心功能&#xff1a; 打开摄像头&#xff08;可支持外接摄像头&#xff09;检测翻页&#xff08;后续考虑添加图像差异算法&#xff09;拍照后用 识图自动用 TextToSpeech 朗读文字内容 &#x1f4cc; 说明&#xff1a;使用了 CameraX&#xff08;Android Jetpack&#xff09;…

园区IPv6规划与部署

​今天我将围绕“园区IPv6规划与部署”这一主题&#xff0c;结合行业趋势、技术难点和实际案例&#xff0c;与大家分享一套可落地的规划方法论。​在开始前&#xff0c;我想先问大家一个问题&#xff1a;​如果现在让你给一个新建园区设计网络&#xff0c;你会优先考虑IPv4还是…

mingw11.2+opencv4.12 cmake contrib编译

第一次Configure之后&#xff0c;会出现不少错误&#xff0c;主要是因为文件没办法正常下载引起的,因为之前编译过vs2022 ,缓存里面有应该下载的文件了&#xff0c;所以这次没有错误&#xff0c;如果你第一次Configure有下载错误&#xff0c;可以下载以下的文件飞书 Docs Link:…

免费MCP服务:Excel CSV 转 JSON MCP by WTSolutions 文档

简介 Excel 转 JSON MCP&#xff08;模型上下文协议&#xff09;提供了一个标准化接口&#xff0c;用于通过模型上下文协议将 Excel 和 CSV 数据转换为 JSON 格式。此 MCP 实现提供了两个专门用于数据转换的工具&#xff1a; excel_to_json_mcp_from_data&#xff1a;转换制表…

应用集成体系深度解析:从数据互通到流程协同

一、应用集成核心概念框架 #mermaid-svg-0V3XAJsofKi2qCa7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-0V3XAJsofKi2qCa7 .error-icon{fill:#552222;}#mermaid-svg-0V3XAJsofKi2qCa7 .error-text{fill:#552222;s…

深入解析 AWS RDS Proxy

在当今微服务架构与无服务器计算快速发展的背景下&#xff0c;数据库连接成为许多应用系统的性能瓶颈。传统RDS实例在处理大量短连接请求时&#xff0c;往往面临连接资源耗尽、连接建立耗时过高等问题。为了解决这一挑战&#xff0c;AWS 推出了 RDS Proxy 服务&#xff0c;通过…

深度剖析 TDMQ RabbitMQ 版经典队列底层存储机制

导语 RabbitMQ 作为开源消息队列的标杆产品&#xff0c;凭借灵活的路由机制与高可用设计&#xff0c;支撑着海量业务场景的消息流转。而经典队列&#xff08;Classic Queue&#xff09; 作为 RabbitMQ 最基础、应用最广泛的队列类型&#xff0c;其底层存储机制直接决定了消息处…

Spring AI开发智能客服(Tool calling)

文章目录前言1 思路分析2 工程结构搭建1_数据库表2_引入依赖3_基础代码3 定义 Tool1_分析查询条件2_定义Function4 系统提示词5 配置ChatClient6 编写Controller7 测试8 Tool calling 底层组件1_ToolCallback2_ToolDefinition3_ToolCallingManager4_ResultConverter5_ToolConte…

设计模式笔记_结构型_适配器模式

1.适配器模式介绍适配器模式是一种结构型设计模式&#xff0c;它允许不兼容的接口协同工作。适配器模式的核心思想是将一个类的接口转换成客户期望的另一个接口&#xff0c;使得原本由于接口不兼容而不能一起工作的类可以一起工作。你可以将其想象成一个“转换插头”——假设你…

事务隔离:从锁实现到MVCC实现

文章目录事务隔离&#xff1a;从锁实现到MVCC实现事务四大特性事务隔离级别锁实现概念实现事务隔离MVCC实现当前读与快照读实现事务隔离Read View总结事务隔离&#xff1a;从锁实现到MVCC实现 面试的时候被面试官问到&#xff1a;你这个项目为什么使用了可重复读而不选择读已提…

小架构step系列18:工具

1 概述 在写代码的时候&#xff0c;有很多通用的、与业务无关逻辑&#xff0c;这些一般写成工具类方法。这些工具类方法慢慢地被积累起来&#xff0c;变成了开源包&#xff0c;可以直接使用开源包&#xff0c;而不是自己再花时间来重复造这些轮子。 这些工具类的开源包比较多…

网络、CentOS 系统、数据库面试知识点总结

文章目录Linux CentOS 面试知识点整理速查复习✅ 一、Linux 高频面试题✅ 二、MySQL 高频面试题✅ 三、计算机网络&#xff08;OSI四层模型&#xff09;高频面试题&#x1f517; 链路层&#xff08;Link Layer&#xff09;&#x1f310; 网络层&#xff08;Internet Layer&…

Vue (Official) v3.0.2 新特性 为非类npm环境引入 globalTypesPath 选项

目录 前言 报错信息 原因 解决方案 总结 前言 在早上更新了vscode后&#xff0c;发现自己 uni-app 项目的 .vue文件 的 template 标签都出现了报错。定位到了问题是因为 Vue (Official) 插件更新导致的&#xff0c;重装了插件的上一个小版本&#xff0c;报错消失&#xff…

程序可能的输出

#include "csapp.h"int main() {int x 3;if (Fork() ! 0)printf("x%d\n", x);printf("x%d\n", --x);exit(0); }分析&#xff1a;父进程先执行printf("x%d\n", x); 输出x4。后执行 printf("x%d\n", --x);输出x3。子进程只执…

2025年UDP应用抗洪指南:从T级清洗到AI免疫,实战防御UDP洪水攻击

一次未防护的UDP暴露&#xff0c;可能让日活百万的应用瞬间瘫痪&#xff0c;损失超千万2025年&#xff0c;随着物联网僵尸网络规模指数级增长及AI驱动的自适应攻击工具泛滥&#xff0c;UDP洪水攻击峰值已突破8Tbps&#xff0c;单次攻击成本却降至50元以下。更致命的是&#xff…