Dubbo SPI底层原理如何彻底搞懂?

摘要:我会用最通俗、最底层、最清晰的方式,把 Dubbo SPI 从是什么 → 为什么要用 → 底层源码流程 → 核心机制讲透,让你一次彻底吃透。 一、先搞懂:什么是 SPI? SPI(Service Provider Interface):服务
我会用最通俗、最底层、最清晰的方式,把 Dubbo SPI 从是什么 → 为什么要用 → 底层源码流程 → 核心机制讲透,让你一次彻底吃透。 一、先搞懂:什么是 SPI? SPI(Service Provider Interface):服务发现机制,接口+配置文件+实现类,程序运行时动态加载接口的实现类,实现模块化、可插拔、可扩展。 简单说: 定义一个接口 写多个实现类 在配置文件里写:key=实现类全类名 运行时通过 key 拿到对应实现类 Java 自带 JDK SPI,但它有 3 个致命缺陷: 一次性加载所有实现,浪费资源 不能根据 key 按需获取实现 不支持 IOC、AOP、默认值、排序等扩展 所以 Dubbo 重写了一套更强大的 SPI,解决了所有问题。 二、Dubbo SPI 核心定位 Dubbo SPI = 可插拔扩展机制 Dubbo 90% 以上的功能(协议、注册中心、负载均衡、序列化、过滤器…)都是基于 SPI 实现的。 它的核心能力: 按需加载,不浪费资源 key-value 配置,按名称获取实现 支持默认实现 支持 IOC 依赖注入 支持 AOP 包装类(Wrapper) 支持排序、自动激活、条件加载 三、Dubbo SPI 使用规范(快速入门) 1. 接口上加注解 @SPI("random") // 默认实现为 random public interface LoadBalance { String select(List<Invoker> invokers); } 2. 配置文件路径(固定) META-INF/dubbo/接口全类名 内容格式: random=com.xxx.RandomLoadBalance roundrobin=com.xxx.RoundRobinLoadBalance 3. 获取实现 // 获取扩展加载器 ExtensionLoader<LoadBalance> loader = ExtensionLoader.getExtensionLoader(LoadBalance.class); // 按 name 获取 LoadBalance lb = loader.getExtension("roundrobin"); // 获取默认实现 LoadBalance defaultLb = loader.getDefaultExtension(); 四、Dubbo SPI 底层原理(核心!逐行拆解) 这是全文最关键部分,我会从类结构 → 缓存 → 加载流程 → 注入 → 包装完整拆解。 核心类:ExtensionLoader Dubbo SPI 唯一入口,一个接口对应一个 ExtensionLoader 实例。 核心流程(底层 8 步) getExtension(name) → 查缓存 → 无则加载配置文件 → 解析类 → 实例化 → IOC 依赖注入 → AOP 包装(Wrapper) → 返回最终对象 下面逐段拆解。 1. 多级缓存(Dubbo 高效的关键) Dubbo SPI 大量使用缓存,避免重复加载、反射创建。 主要缓存: cachedInstances:name → 扩展类实例(最终对象) cachedClasses:name → 扩展类 Class cachedWrappers:包装类列表 cachedAdaptiveInstance:自适应拓展对象 2. 加载配置文件(底层扫描路径) Dubbo 会扫描 3 个固定路径: META-INF/dubbo/ META-INF/dubbo/internal/ (Dubbo 内部使用) META-INF/services/ (兼容 JDK SPI) 解析规则: 忽略 # 注释 格式 key=value 重复 key 后面覆盖前面 3. 实例化扩展类 简单调用: clazz.newInstance() 4. IOC 依赖注入(自动装配) Dubbo 会自动扫描扩展类的 set 方法,自动注入其他扩展。
阅读全文