Dubbo 的 SPI 扩展机制 是其插件化架构的核心,其中:
@Adaptive
注解用于生成 自适应扩展类的字节码Wrapper
链用于实现 功能增强(类似装饰器模式)- 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
}
步骤:
- 解析接口中的所有
@Adaptive
方法。 - 构建 Java 源码字符串。
- 使用 Javassist 或其他编译器将源码编译为
.class
文件。 - 加载该类并返回。
二、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 的源码细节,建议从以下几个方向入手:
-
调试
ExtensionLoader
的加载过程- 设置断点观察如何创建
Protocol$Adaptive
- 观察 Wrapper 链的构建顺序
- 设置断点观察如何创建
-
查看
AdaptiveClassCodeGenerator
源码- 分析它是如何生成 Java 源码的
-
阅读
ProtocolFilterWrapper
等 Wrapper 实现- 理解责任链模式在 Dubbo 中的应用
-
尝试编写一个自己的 Filter 或 LoadBalance 实现
- 通过 SPI 注册并测试其调用流程
-
集成 Spring Boot 并使用
@Reference
引用服务- 查看 Spring Boot 是如何触发 Dubbo 初始化的