← 返回文章列表

Dubbo 底层原理系列(二):SPI 机制

2020-07-15·5 分钟阅读

前言

SPI(Service Provider Interface)是 Dubbo 框架的核心设计理念,实现了框架的高度可扩展性。Dubbo 的所有组件都是通过 SPI 加载的。

技术亮点

技术点难度面试价值本文覆盖
ExtensionLoader⭐⭐⭐⭐高频考点
自适应扩展⭐⭐⭐⭐⭐进阶考点
扩展点激活⭐⭐⭐⭐进阶考点
IOC/AOP⭐⭐⭐⭐进阶考点

面试考点

  1. Dubbo SPI 与 Java SPI 有什么区别?
  2. ExtensionLoader 是如何加载扩展的?
  3. 什么是自适应扩展?如何实现的?
  4. @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扩展点自动激活
IOCsetter 方法依赖注入
AOPWrapper 装饰器模式

参考资料

下一章预告

下一章将深入解析 服务暴露原理,包括:

  • 服务导出流程
  • URL 解析与转换
  • 注册中心注册
分享: