Dubbo 的 SPI(二)

Dubbo 的 SPI 扩展机制 是其插件化架构的核心,其中:

  1. @Adaptive 注解用于生成 自适应扩展类的字节码
  2. Wrapper 链用于实现 功能增强(类似装饰器模式)
  3. Dubbo 与 Spring Boot 整合时,使用了 SPI 和 Spring 的自动装配机制

一、@Adaptive 自适应扩展类的字节码生成原理

1. 背景

Dubbo 中很多接口都有多个实现,例如 Protocol 接口有 dubbo, rmi, http 等协议。但某些方法需要根据运行时参数(如 URL)决定使用哪个具体实现。

为此 Dubbo 提供了 @Adaptive 注解,用于标记某个方法或类为“自适应”,在运行时动态选择具体的实现。

2. 自动生成的 Adaptive 类示例

Protocol 接口为例:

@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
}

Dubbo 会生成一个名为 Protocol$Adaptive 的类,内容大致如下(伪代码):

public class Protocol$Adaptive implements Protocol {
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (invoker == null) throw new IllegalArgumentException("...");
        URL url = invoker.getUrl();
        String protocolName = url.getParameter("protocol", "dubbo");
        Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(protocolName);
        return protocol.export(invoker);
    }

    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (type == null || url == null) throw new IllegalArgumentException("...");
        String protocolName = url.getParameter("protocol", "dubbo");
        Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(protocolName);
        return protocol.refer(type, url);
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException("method not supported!");
    }
}

3. 字节码生成流程解析

Dubbo 使用 Javassist 或 JDK 动态代理 来生成 @Adaptive 类的字节码。

核心逻辑位于:
private Class<?> createAdaptiveExtensionClass() {
    String code = new AdaptiveClassCodeGenerator(interface, methods).generate(); // 生成 Java 源码
    ClassLoader classLoader = ...;
    Compiler compiler = ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();
    return compiler.compile(code, classLoader); // 编译成 Class
}
步骤:
  1. 解析接口中的所有 @Adaptive 方法。
  2. 构建 Java 源码字符串。
  3. 使用 Javassist 或其他编译器将源码编译为 .class 文件。
  4. 加载该类并返回。

二、Wrapper 链的处理机制

1. Wrapper 类的作用

Wrapper 类用于对原始扩展进行包装和增强,类似于 装饰器模式。例如:

public class ProtocolFilterWrapper implements Protocol {
    private final Protocol protocol;

    public ProtocolFilterWrapper(Protocol protocol) {
        this.protocol = protocol;
    }

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        return protocol.export(new FilterInvokerListenerExporter(invoker));
    }
}

2. Wrapper 类的识别规则

如果某个扩展类满足以下条件之一,会被认为是 Wrapper 类:

  • 构造函数只有一个参数,且类型是接口本身(如 Protocol
  • 类上有 @Wrapper 注解,并指定了匹配的参数值(可选)

3. Wrapper 链的构建过程

createExtension() 方法中,会判断是否是 Wrapper 类,并层层包装:

T instance = (T) clazz.newInstance();
injectExtension(instance); // IOC 注入依赖
for (Object wrapper : wrapperInstances) {
    instance = injectExtension((T) wrapper); // 包装
}

最终形成链式结构,例如:

Protocol$Adaptive
   └── ProtocolFilterWrapper
       └── ProtocolListenerWrapper
           └── DubboProtocol

三、Dubbo SPI 如何结合 Spring Boot 使用

1. Dubbo 与 Spring Boot 整合方式

Dubbo 支持与 Spring Boot 的无缝整合,主要通过以下方式:

  • @Service 注解暴露服务
  • @Reference 注解引用远程服务
  • application.properties 配置 Dubbo 参数
  • 自动加载 Dubbo 配置与 SPI 扩展

2. dubbo-spring-boot-starter 的作用

Spring Boot Starter 是 Dubbo 提供的自动装配模块,核心组件包括:

组件作用
DubboAutoConfiguration自动注册 Dubbo 相关 Bean
DubboBootstrapApplicationListener监听 Spring 容器事件启动 Dubbo 服务
ServiceBean对应 @Service,负责服务导出
ReferenceBean对应 @Reference,负责服务引用

3. SPI 在 Spring Boot 中的应用

虽然 Dubbo 使用 SPI 实现插件化,但在 Spring Boot 中也可以通过以下方式混合使用:

✅ 保留 SPI 扩展点
  • META-INF/dubbo/ 下定义扩展点
  • Dubbo 依然通过 ExtensionLoader 加载这些扩展
✅ 使用 Spring Bean 替代部分 SPI 扩展
  • 可以通过 @Component 将 SPI 扩展作为 Spring Bean 注册
  • Dubbo 内部会优先使用 Spring Bean(如果启用 spring 集成)
✅ 示例:自定义 Filter + Spring 注入
@Component
public class MyCustomFilter implements Filter {
    @Autowired
    private MyBusinessService businessService;

    public Result invoke(Invoker<?> invoker, Invocation invocation) {
        businessService.doSomething();
        return invoker.invoke(invocation);
    }
}

配置文件中启用:

# META-INF/dubbo/org.apache.dubbo.rpc.Filter
mycustom=com.example.MyCustomFilter

四、总结

特性描述
@Adaptive 生成根据接口方法动态生成 Java 源码并编译为 Class
Wrapper 处理通过构造函数识别 Wrapper 并层层包装
Spring Boot 整合使用 dubbo-spring-boot-starter 自动装配
SPI 与 Spring 兼容支持 SPI 扩展,也可用 Spring Bean 替代
自定义扩展可以注入 Spring Bean,支持 AOP、IOC

五、推荐学习路径

如果想深入研究 Dubbo SPI 的源码细节,建议从以下几个方向入手:

  1. 调试 ExtensionLoader 的加载过程

    • 设置断点观察如何创建 Protocol$Adaptive
    • 观察 Wrapper 链的构建顺序
  2. 查看 AdaptiveClassCodeGenerator 源码

    • 分析它是如何生成 Java 源码的
  3. 阅读 ProtocolFilterWrapper 等 Wrapper 实现

    • 理解责任链模式在 Dubbo 中的应用
  4. 尝试编写一个自己的 Filter 或 LoadBalance 实现

    • 通过 SPI 注册并测试其调用流程
  5. 集成 Spring Boot 并使用 @Reference 引用服务

    • 查看 Spring Boot 是如何触发 Dubbo 初始化的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值