
一、前言
Dubbo 作为主流 RPC 框架,SPI(Service Provider Interface) 是其核心可扩展基石。相比 JDK 原生 SPI,Dubbo SPI 实现了按需加载、自动装配、AOP 包装、自适应扩展、激活扩展等增强能力,支撑协议、序列化、过滤器、路由等组件灵活替换。本文基于源码深度总结 Dubbo SPI 完整流程、核心类、IOC/AOP 实现、自适应与激活机制,帮你彻底掌握其扩展原理。
二、Dubbo SPI 核心基础
1. 核心角色
- ExtensionLoader:扩展点加载器,单个接口对应一个 Loader,负责加载、创建、缓存、注入、包装扩展实例。
- ExtensionFactory:对象工厂,从 Dubbo SPI 或 Spring 容器获取对象,支撑依赖注入。
- 扩展点接口 :被
@SPI标记的接口(如 Protocol、Filter)。 - 扩展实现类:配置在 META-INF 目录下、对应接口的具体实现。
2. 与 JDK SPI 关键差异
- JDK SPI:一次性加载所有实现,无法按需获取;不支持 IOC/AOP;无自适应/激活机制。
- Dubbo SPI:按 name 加载;支持缓存、依赖注入、包装器;提供 Adaptive 动态适配、Activate 条件激活。
三、Dubbo SPI 核心流程(从调用到返回)
1. 最简使用 Demo
java
ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
Protocol protocol = loader.getExtension("dubbo");
执行路径:获取 Loader → 加载扩展类 → 创建实例 → IOC 注入 → AOP 包装 → 返回实例。
2. 核心方法链路
getExtensionLoader(Class<T> type):全局缓存EXTENSION_LOADERS,保证一个接口只生成一个 Loader。getExtension(String name):先查缓存cachedInstances,命中直接返回;未命中执行createExtension。createExtension(String name):核心创建流程。getExtensionClasses():加载并缓存所有扩展实现类。
四、ExtensionLoader 核心属性
static ConcurrentHashMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS:Loader 全局缓存。ConcurrentHashMap<String, Object> cachedInstances:扩展实例缓存。Class<?> type:当前 Loader 对应的扩展接口。ExtensionFactory objectFactory:对象工厂,用于 IOC 注入。Map<String, Class<?>> extensionClasses:name→ 实现类映射。Set<Class<?>> cachedWrapperClasses:AOP 包装器集合。Class<?> cachedAdaptiveClass:自适应扩展类。Map<String, Class<?>> cachedActivates:激活扩展集合。String cachedDefaultName:默认扩展 name(来自@SPI("xxx"))。
五、扩展类加载流程(getExtensionClasses)
1. 加载目录(优先级从高到低)
- META-INF/dubbo/internal/
- META-INF/dubbo/
- META-INF/services/(兼容 JDK SPI)
2. 加载步骤
- 按接口全限定名读取配置文件。
loadResource解析行:name=实现类全限定名。loadClass分类处理:- 标记
@Adaptive→ 存入cachedAdaptiveClass。 - 是 Wrapper 包装类(单参构造,参数为接口)→ 存入
cachedWrapperClasses。 - 标记
@Activate→ 存入cachedActivates。 - 普通实现 → 存入
extensionClasses。
- 标记
- 结果缓存,避免重复加载。
六、实例创建流程(createExtension)
- 找类 :根据 name 从
extensionClasses获取实现类。 - 实例化:反射 newInstance,放入缓存。
- IOC 依赖注入:自动 setter 注入依赖对象。
- AOP 包装:按顺序套 Wrapper 包装器。
- 返回:返回最终包装后的实例。
示例:DubboProtocol → ProtocolFilterWrapper → ProtocolListenerWrapper
七、Dubbo IOC(依赖注入)实现
1. 注入逻辑
- 扫描实例的setter 方法。
- 获取参数类型 pt 与属性名 property。
- 调用
objectFactory.getExtension(pt, property)获取依赖。 - 反射执行 setter 赋值。
2. 关键特点
- 只支持 setter 注入。
- 注入的是**自适应扩展对象(代理)**,运行时才按 URL 确定真实实现。
- 可从 Dubbo SPI 或 Spring 容器获取依赖。
八、Dubbo AOP(Wrapper 包装)实现
1. Wrapper 判定规则
类包含单参数构造方法,参数为扩展接口类型,即为包装器。
2. 包装流程
- 实例化目标扩展并完成 IOC。
- 遍历所有 Wrapper,依次嵌套包裹。
- 每个 Wrapper 包裹后再次执行 IOC。
- 返回最外层包装对象。
作用:统一切面逻辑(过滤、监听、监控),不侵入业务实现。
九、自适应扩展(Adaptive)机制
1. 作用
运行时根据 URL 动态确定使用哪个扩展实现,无需硬编码。
2. 两种实现方式
- 手动指定 :实现类上加
@Adaptive,直接作为代理类。 - 自动生成 :Dubbo 动态生成
接口名$Adaptive代理类。
3. 自动生成规则
- 仅对带
@Adaptive的方法生成代理逻辑。 - 方法必须能获取URL(参数是 URL/参数含 getUrl())。
- 从 URL 提取扩展 name,再加载真实实现调用。
4. 典型代理代码逻辑
java
String extName = url.getProtocol() == null ? "dubbo" : url.getProtocol();
Protocol extension = loader.getExtension(extName);
return extension.export(invoker);
十、激活扩展(Activate)机制
1. 作用
按 URL 参数、分组一次性获取多个匹配的扩展(如 Filter 链)。
2. @Activate 属性
group:限定提供者(PROVIDER)/消费者(CONSUMER)。value:URL 参数 key,包含则激活。order:排序用。
3. 获取方式
java
List<Filter> filters = loader.getActivateExtension(url, values, CONSUMER);
4. 匹配来源
group匹配的默认激活扩展。- URL 包含
value参数的扩展。 - 手动指定 name 的扩展。
十一、总结
Dubbo SPI 通过 ExtensionLoader 统一管理扩展生命周期,以缓存 + 按名加载 提升性能,用 IOC 注入 解耦依赖,AOP 包装 实现切面增强,Adaptive 实现动态适配,Activate 实现条件激活,构成完整可扩展体系,是 Dubbo 高灵活、易扩展的核心引擎。