Dubbo 底层原理系列(二):SPI 机制
2020-07-15·5 分钟阅读
前言
SPI(Service Provider Interface)是 Dubbo 框架的核心设计理念,实现了框架的高度可扩展性。Dubbo 的所有组件都是通过 SPI 加载的。
技术亮点
| 技术点 | 难度 | 面试价值 | 本文覆盖 |
|---|---|---|---|
| ExtensionLoader | ⭐⭐⭐⭐ | 高频考点 | ✅ |
| 自适应扩展 | ⭐⭐⭐⭐⭐ | 进阶考点 | ✅ |
| 扩展点激活 | ⭐⭐⭐⭐ | 进阶考点 | ✅ |
| IOC/AOP | ⭐⭐⭐⭐ | 进阶考点 | ✅ |
面试考点
- Dubbo SPI 与 Java SPI 有什么区别?
- ExtensionLoader 是如何加载扩展的?
- 什么是自适应扩展?如何实现的?
- @Activate 注解的作用是什么?
Java SPI vs Dubbo SPI
对比分析
┌─────────────────────────────────────────────────────────────────────────┐
│ Java SPI vs Dubbo SPI │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Java SPI: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ // 配置文件位置:META-INF/services/接口全限定名 │ │
│ │ // 文件内容:实现类全限定名 │ │
│ │ │ │
│ │ // 加载方式 │ │
│ │ ServiceLoader<Robot> loader = ServiceLoader.load(Robot.class); │ │
│ │ for (Robot robot : loader) { │ │
│ │ robot.sayHello(); │ │
│ │ } │ │
│ │ │ │
│ │ 问题: │ │
│ │ • 一次性加载所有实现,浪费资源 │ │
│ │ • 无法按名称获取指定实现 │ │
│ │ • 不支持依赖注入和 AOP │ │
│ │ • 扩展点无法获取配置 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Dubbo SPI: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ // 配置文件位置:META-INF/dubbo/接口全限定名 │ │
│ │ // 文件内容:name=实现类全限定名 │ │
│ │ │ │
│ │ // 加载方式 │ │
│ │ ExtensionLoader<Protocol> loader = │ │
│ │ ExtensionLoader.getExtensionLoader(Protocol.class); │ │
│ │ Protocol protocol = loader.getExtension("dubbo"); │ │
│ │ │ │
│ │ 优势: │ │
│ │ • 按需加载,性能更好 │ │
│ │ • 支持按名称获取实现 │ │
│ │ • 支持依赖注入(IOC) │ │
│ │ • 支持装饰器模式(AOP) │ │
│ │ • 支持自适应扩展 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
ExtensionLoader 核心原理
类结构
┌─────────────────────────────────────────────────────────────────────────┐
│ ExtensionLoader 核心结构 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ExtensionLoader<T> │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ // 缓存 │ │ │
│ │ │ Map<String, Class<?>> cachedClasses // 扩展类缓存 │ │ │
│ │ │ Map<String, Holder<Object>> cachedInstances // 实例缓存 │ │ │
│ │ │ Holder<Object> cachedAdaptiveInstance // 自适应实例 │ │ │
│ │ │ Map<Class<?>, String> cachedNames // 类名缓存 │ │ │
│ │ │ │ │ │
│ │ │ // 核心方法 │ │ │
│ │ │ getExtension(String name) // 获取扩展实例 │ │ │
│ │ │ getAdaptiveExtension() // 获取自适应扩展 │ │ │
│ │ │ getActivateExtension() // 获取激活扩展 │ │ │
│ │ │ getExtensionClasses() // 加载扩展类 │ │ │
│ │ │ │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
加载流程
┌─────────────────────────────────────────────────────────────────────────┐
│ ExtensionLoader 加载流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ getExtension("dubbo") │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 1. 检查 cachedInstances 缓存 │ │
│ │ │ │ │
│ │ ├──► 命中:直接返回 │ │
│ │ │ │ │
│ │ └──► 未命中:创建 Holder,继续 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 2. 双重检查锁创建实例 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ createExtension(name) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 3. 获取扩展类 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ getExtensionClasses() │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ loadExtensionClasses() │ │
│ │ │ │ │
│ │ ├──► META-INF/dubbo/internal/ │ │
│ │ ├──► META-INF/dubbo/ │ │
│ │ └──► META-INF/services/ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 4. 实例化并注入依赖 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ clazz.newInstance() │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ injectExtension(instance) // IOC 注入 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ wrapper 包装(AOP) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 返回扩展实例 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
配置文件格式
# META-INF/dubbo/org.apache.dubbo.rpc.Protocol
# 协议名=实现类
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=org.apache.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=org.apache.dubbo.rpc.protocol.hessian.HessianProtocol
http=org.apache.dubbo.rpc.protocol.http.HttpProtocol
# 自适应扩展类(带 @Adaptive 注解)
adaptive=org.apache.dubbo.rpc.protocol.Protocol$Adaptive
# 包装类(带 Wrapper 构造函数)
filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
自适应扩展
@Adaptive 注解
┌─────────────────────────────────────────────────────────────────────────┐
│ 自适应扩展机制 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 自适应扩展:根据 URL 参数动态选择实现 │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ @SPI("dubbo") │ │
│ │ public interface Protocol { │ │
│ │ │ │
│ │ // 自适应方法,根据 URL 中的 protocol 参数选择实现 │ │
│ │ @Adaptive │ │
│ │ void export(URL url); │ │
│ │ │ │
│ │ @Adaptive │ │
│ │ Invoker refer(Class type, URL url); │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 生成的自适应类(编译后): │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ public class Protocol$Adaptive implements Protocol { │ │
│ │ public void export(URL url) { │ │
│ │ // 从 URL 获取 protocol 参数,默认 dubbo │ │
│ │ String extName = url.getProtocol() == null ? │ │
│ │ "dubbo" : url.getProtocol(); │ │
│ │ │ │
│ │ // 根据名称获取扩展 │ │
│ │ Protocol extension = ExtensionLoader │ │
│ │ .getExtensionLoader(Protocol.class) │ │
│ │ .getExtension(extName); │ │
│ │ │ │
│ │ extension.export(url); │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
自适应扩展生成流程
┌─────────────────────────────────────────────────────────────────────────┐
│ 自适应扩展生成流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ getAdaptiveExtension() │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 1. 检查缓存 │ │
│ │ │ │ │
│ │ ├──► 命中:返回 │ │
│ │ │ │ │
│ │ └──► 未命中:继续 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 2. 查找 @Adaptive 注解的类 │ │
│ │ │ │ │
│ │ ├──► 找到:直接实例化 │ │
│ │ │ │ │
│ │ └──► 未找到:动态生成 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 3. 动态生成代码 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ createAdaptiveExtensionClassCode() │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 遍历方法,生成代理逻辑 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 4. 编译加载 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ compiler.compile(code) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Class.forName() 加载 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 返回自适应实例 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
扩展点激活
@Activate 注解
┌─────────────────────────────────────────────────────────────────────────┐
│ @Activate 注解 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ @Activate 用于标记可以被自动激活的扩展点: │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ @Activate( │ │
│ │ group = {"consumer", "provider"}, // 生效的分组 │ │
│ │ value = {"cache", "validation"}, // URL 中包含的 key │ │
│ │ order = 1, // 排序 │ │
│ │ before = {"filterA"}, // 在某个扩展之前 │ │
│ │ after = {"filterB"} // 在某个扩展之后 │ │
│ │ ) │ │
│ │ public class CacheFilter implements Filter { │ │
│ │ // ... │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 使用场景: │
│ • Filter 过滤器链自动组装 │
│ • 按条件激活特定扩展 │
│ • 扩展点排序 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
激活流程
┌─────────────────────────────────────────────────────────────────────────┐
│ 激活扩展流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ getActivateExtension(url, keys, group) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 1. 加载所有扩展类 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ getExtensionClasses() │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 2. 遍历检查 @Activate 注解 │ │
│ │ │ │ │
│ │ ├──► group 匹配检查 │ │
│ │ │ │ │
│ │ ├──► value(URL key)匹配检查 │ │
│ │ │ │ │
│ │ └──► 添加到激活列表 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 3. 排序 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 按 order、before、after 排序 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 返回激活的扩展实例列表 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
IOC 与 AOP
IOC 依赖注入
┌─────────────────────────────────────────────────────────────────────────┐
│ IOC 依赖注入 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Dubbo SPI 支持 setter 方法注入: │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ public class DubboProtocol implements Protocol { │ │
│ │ │ │
│ │ // 自适应扩展注入 │ │
│ │ private ProxyFactory proxyFactory; │ │
│ │ │ │
│ │ public void setProxyFactory(ProxyFactory proxyFactory) { │ │
│ │ this.proxyFactory = proxyFactory; │ │
│ │ } │ │
│ │ │ │
│ │ // 普通扩展注入 │ │
│ │ private ThreadPool threadPool; │ │
│ │ │ │
│ │ public void setThreadPool(ThreadPool threadPool) { │ │
│ │ this.threadPool = threadPool; │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 注入规则: │
│ • 方法必须为 public setter │ │
│ • 参数类型为 SPI 接口 │ │
│ • 参数为 Adaptive 注入自适应扩展 │ │
│ • 参数指定 name 注入指定扩展 │ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
AOP 装饰器
┌─────────────────────────────────────────────────────────────────────────┐
│ AOP 装饰器模式 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Dubbo 通过 Wrapper 类实现 AOP: │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ // ProtocolFilterWrapper │ │
│ │ public class ProtocolFilterWrapper implements Protocol { │ │
│ │ │ │
│ │ private final Protocol protocol; // 被装饰对象 │ │
│ │ │ │
│ │ // 构造函数注入原始 Protocol │ │
│ │ public ProtocolFilterWrapper(Protocol protocol) { │ │
│ │ this.protocol = protocol; │ │
│ │ } │ │
│ │ │ │
│ │ @Override │ │
│ │ public void export(URL url) { │ │
│ │ // 前置处理:构建 Filter 链 │ │
│ │ // ... │ │
│ │ protocol.export(url); // 调用原始方法 │ │
│ │ // 后置处理 │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Wrapper 识别规则: │
│ • 类有拷贝构造函数:public Xxx(Xxx xxx) │ │
│ • 实现 SPI 接口 │ │
│ • 不标记为 @Adaptive │ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
总结
本文介绍了 Dubbo SPI 机制:
| 概念 | 说明 |
|---|---|
| ExtensionLoader | 扩展点加载器 |
| @Adaptive | 自适应扩展,根据 URL 动态选择 |
| @Activate | 扩展点自动激活 |
| IOC | setter 方法依赖注入 |
| AOP | Wrapper 装饰器模式 |
参考资料
下一章预告
下一章将深入解析 服务暴露原理,包括:
- 服务导出流程
- URL 解析与转换
- 注册中心注册
相关文章
Dubbo 底层原理系列(七):集群容错
2020-09-03·5 分钟阅读
深入解析 Dubbo 集群容错原理,包括 Failover、Failfast、Failsafe、Failback、Forking、Broadcast 等容错模式。
Dubbo 底层原理系列(六):负载均衡
2020-08-25·5 分钟阅读
深入解析 Dubbo 负载均衡原理,包括 Random、RoundRobin、LeastActive、ConsistentHash 策略的实现细节。
Dubbo 底层原理系列(四):服务引用
2020-08-03·5 分钟阅读
深入解析 Dubbo 服务引用原理,包括服务引用流程、代理对象创建、Directory 与 Cluster Invoker 构建。