Java SPI、Spring SPI 和 Dubbo SPI:扩展机制

SPI全称为Service Provider Interface,是一种动态替换发现的机制,一种解耦非常优秀的思想,SPI可以很灵活的让接口和实现分离,让api提供者只提供接口,第三方来实现,然后可以使用配置文件的方式来实现替换或者扩展,在框架中比较常见,提高框架的可扩展性。

简单来说SPI是一种非常优秀的设计思想,它的核心就是解耦、方便扩展。

Java SPI

约定规范

  • 文件必须放在META-INF/services/目录底下
  • 文件名必须为接口的全限定名,内容为接口实现的全限定名
ini 复制代码
// 加载扩展类
ServiceLoader<ISearch> serviceLoader = ServiceLoader.load(ISearch.class);

优缺点

一次性实例化所有扩展实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。

不能按需加载某个类。

Spring SPI

约定规范

  • 文件必须放在META-INF/目录底下,文件名为spring.factories
  • 文件内容为键值对 键为接口的全限定名,值为接口实现的全限定名,多个实现逗号分隔
  • 注意:Springboot3.0之后可在 META-INF/spring/目录下的org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中,内容为接口实现全限定名
ini 复制代码
// 加载扩展点 PropertySourceLoader是用来解析application配置文件的
List<PropertySourceLoader> propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader());
// 获取类限定名
List<String> strings = SpringFactoriesLoader.loadFactoryNames(PropertySourceLoader.class, getClass().getClassLoader());

与Java SPI 进行对比

Spring SPI 不同的接口扩展都写在一个文件里,不像Java SPI 不同接口需要创建不同文件。 Spring SPI 提供了获取类限定名方法。可以获取到类限定名之后就可以将这些类注入到Spring容器中,用Spring容器加载这些Bean,而不仅仅是通过反射。

Spring SPI 一样没有实现按需加载,需要自己实现加载接口哪个具体实现,如接口设计为策略接口,或者实现类加入优先级。不管怎样,都需要自己实现

Dubbo SPI

约定规范

  • 接口必须要加@SPI注解
  • 配置文件可以放在META-INF/services/、META-INF/dubbo/internal/、META-INF/dubbo/ 、META-INF/dubbo/external/这四个目录底下,文件名也是接口的全限定名
  • 内容为键值对,键为短名称(可以理解为spring中Bean的名称),值为实现类的全限定名

Dubbo SPI 基础

ini 复制代码
// 加载扩展点 IBusinessService
ExtensionLoader<IBusinessService> extensionLoader = ExtensionLoader.getExtensionLoader(IBusinessService.class);
// 获取默认扩展点
IBusinessService defaultExtension = extensionLoader.getDefaultExtension();
// 获取指定扩展点
IBusinessService bExtension = extensionLoader.getExtension("a");

Dubbo SPI 自适应扩展

静态方式

静态Adaptive类:Adaptive类在项目中是存在的,不需要去动态生成

实现方式:类上面加入@Adaptive 注解, 如:AdaptiveCompiler 就是 Compiler的自适应扩展类

ini 复制代码
Compiler compiler = ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();

动态方式

动态Adaptive类:动态Adaptive类是在调用的时候,实时生成的字节码,再通过动态反射创建Adaptive类。 如Protocol协议接口,实时动态生成Protocol$Adaptive.class实例

ini 复制代码
// 动态生成Protocol$Adaptive.class实例
Protocol adaptiveProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
System.out.println(adaptiveProtocol);
// 当调用export、refer方法时,根据URL协议自适应找到协议具体实现,如解析URL协议为dubbo,伪代码如下
Protocol dubbo = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");

Dubbo SPI 自动激活

自动激活,注解@Activate, 就是根据你的入参,动态地选择一批实现类

在Dubbo一个核心的使用场景就是Filter过滤器链中。Filter有很多实现,为了能够区分Filter的实现是作用于provider的还是consumer端,所以就可以用自动激活的机制来根据入参来动态选择一批Filter实现,

如过滤器GenericFilter

kotlin 复制代码
@Activate(group = CommonConstants.PROVIDER, order = -20000)
public class GenericFilter {

 //...

}
相关推荐
wuminyu6 小时前
JVM规范学习总结:解释器与JIT编译器如何处理JVM字节码
java·linux·c语言·jvm·c++
txxzjmzlh6 小时前
SpringBoot 配置⽂件
java·spring boot·spring
Wang's Blog7 小时前
Java 项目实战: 交友项目 - 搜索附近的功能概述和分析
java·开发语言·交友
骑着蜗牛撵大象3277 小时前
大模型 Agent 的沙箱护栏:gVisor 隔离、出口白名单与审计闭环
java·开发语言·jvm·安全·agent沙箱·gvisor·microvm
独泪了无痕7 小时前
Hutool之LocalDateTimeUtil:让时间间隔操作如虎添翼
java
三无少女指南8 小时前
从这里开始:Java 后端与 AI 工程的三条阅读路线
java·学习路线·ai工程
香蕉大香蕉(06敬畏市场)9 小时前
MySQL 单点详解(07)
java·数据库·mysql·ai·博客·八股
nice_lcj5209 小时前
深入理解 Java 反射:从 Class 对象到方法调用(含性能实测 + 迷你 IOC 实战 + 面试考点)
java·开发语言·后端·面试
骑着蜗牛撵大象3279 小时前
2026 后端 AI 工程化:Spring AI 管调用、MCP 管工具、Agent 内嵌管自治
java·人工智能·后端·spring·ai·可观测性·mcp
tryxr9 小时前
JVM 垃圾回收机制
java·jvm·gc·垃圾回收