首页
/ Apache Dubbo dubbo-compatible 兼容模块解析:com.alibaba.dubbo 到 org.apache.dubbo 的平滑迁移

Apache Dubbo dubbo-compatible 兼容模块解析:com.alibaba.dubbo 到 org.apache.dubbo 的平滑迁移

2026-09-05 23:37:03作者:柯茵沙

导读

Apache Dubbo 从 2.7.x 版本起将包名从 com.alibaba.dubbo 迁移到 org.apache.dubbo,这对既有代码意味着大规模的 import 变更。本仓库通过 dubbo-compatible 模块提供了一套 com.alibaba.dubbo 命名空间下的兼容(bridge)API,使旧代码可以在不做(或少做)改动的前提下继续运行。本文以 dubbo-compatible/README.md 为主体,结合模块内的桥接实现源码与测试用例,讲解:兼容模块覆盖了哪些常用 API、这些 API 是如何"桥接"到新包名的、哪些 API 有单元测试保障、以及官方给出的迁移建议与边界。

一、背景:包名迁移与兼容模块的定位

dubbo-compatible/README.md 开篇说明了该模块存在的原因:

From 2.7.x, Dubbo has renamed package to org.apache.dubbo, so dubbo-compatible module is provided.

也就是说,Dubbo 捐赠给 Apache 基金会后,2.7.x 将核心包从 com.alibaba.dubbo 重命名为 org.apache.dubbo。对于已经深度依赖旧包名的项目,一次性全量替换 import 风险较高,因此官方提供了 dubbo-compatible 模块,在其中"复刻"了最常用的旧 API(类/接口),供旧版本代码继续编译与运行。

需要注意的关键边界(同样来自 README,必须牢记):

  1. 有单元测试保障的只是一部分"最常用"的 API——README 明确列出了一份清单(见第二节);
  2. 清单之外的 bridge API 没有任何单元测试,README 原文提示 "they may work with wrong"(可能行为不正确);
  3. 官方建议新代码使用新 API 实现扩展(README 标注为 RECOMMENDED),并明确声明 "We will remove this module some day"——兼容模块是临时性的过渡工具,未来会被移除。

二、有单元测试保障的常用 API 清单

README 列出的"most popular APIs"共三组,全部可以在 dubbo-compatible/src/main/java/com/alibaba/dubbo 下找到对应源码文件:

旧包 API 仓库中对应源码
com.alibaba.dubbo.rpc.Filter Filter.java
com.alibaba.dubbo.rpc.Invocation Invocation.java
com.alibaba.dubbo.rpc.Invoker Invoker.java
com.alibaba.dubbo.rpc.Result Result.java
com.alibaba.dubbo.rpc.RpcContext RpcContext.java
com.alibaba.dubbo.rpc.RpcException RpcException.java
com.alibaba.dubbo.config.annotation.Reference / Service Reference.java / Service.java
com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo EnableDubbo.java
com.alibaba.dubbo.common.Constants / URL Constants.java / URL.java
com.alibaba.dubbo.common.extension.ExtensionFactory ExtensionFactory.java
com.alibaba.dubbo.common.serialize.Serialization / ObjectInput / ObjectOutput serialize 目录
com.alibaba.dubbo.rpc.service.EchoService / GenericService rpc/service 目录

README 还提到 com.alibaba.dubbo.cache.CacheFactory / Cache。此外,从源码结构看,模块内实际提供的兼容类远不止上表——com.alibaba.dubbo 下还包含 configApplicationConfigReferenceConfigServiceConfigRegistryConfig 等)、registryRegistryRegistryFactory)、remotingChannelCodecTransporter 等)、rpc.clusterClusterRouterLoadBalance 等)、container.page(QoS 控制台页面)等桥接类。但按 README 的定义,这些额外类属于"无单元测试的 bridge API",行为正确性没有保障,应优先迁移到新 API。

上述核心 API 的单元测试位于测试根目录 src/test/java/org/apache/dubbo 下,README 所称 "The above APIs work fine with some unit tests in the test root" 可对照这些测试验证:

三、桥接机制源码解析:旧 API 如何落到新实现

3.1 接口继承是桥接的第一层

以最常用的 Filter 为例(Filter.java):

@Deprecated
public interface Filter extends org.apache.dubbo.rpc.Filter {

    Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;

    @Override
    default org.apache.dubbo.rpc.Result invoke(
            org.apache.dubbo.rpc.Invoker<?> invoker, org.apache.dubbo.rpc.Invocation invocation)
            throws org.apache.dubbo.rpc.RpcException {
        Result invokeResult =
                invoke(new Invoker.CompatibleInvoker<>(invoker),
                       new Invocation.CompatibleInvocation(invocation));

        if (invokeResult instanceof Result.CompatibleResult) {
            return ((Result.CompatibleResult) invokeResult).getDelegate();
        }
        // 否则把旧 Result 的 value/exception/attachments 搬到 AsyncRpcResult 上
        AsyncRpcResult asyncRpcResult = AsyncRpcResult.newDefaultAsyncResult(invocation);
        ...
        return asyncRpcResult;
    }
}

这里的设计可以概括为三层:

  1. 继承新接口:旧 Filter 直接 extends org.apache.dubbo.rpc.Filter,因此旧 Filter 的 SPI 实现(如 META-INF/dubbo/ 下注册的 com.alibaba.dubbo.rpc.Filter 扩展)能被新框架的扩展加载器直接当作 org.apache.dubbo.rpc.Filter 使用;
  2. default 方法做方向翻译:新框架调用的是 org.apache.dubbo.rpc.Invoker/Invocation 参数的 invoke。兼容接口的 default 实现把这些"新对象"包装成 CompatibleInvoker / CompatibleInvocation,再回调用户只实现了"旧签名" invoke(Invoker, Invocation) 的扩展逻辑;
  3. 结果反向解包:如果旧实现返回 Result.CompatibleResult(内部持有 delegate),直接取 getDelegate() 还原出新 Result;否则用 AsyncRpcResult 把 value、exception、attachments 逐项搬过去。

@Deprecated 注解明确表达了"旧 API 仅用于过渡"的定位。同样的模式贯穿整个模块:Invocation extends org.apache.dubbo.rpc.InvocationInvoker<T> extends org.apache.dubbo.rpc.Invoker<T>Result extends org.apache.dubbo.rpc.Result(见 Result.java)、ExtensionFactory extends org.apache.dubbo.common.extension.ExtensionFactorySerialization extends org.apache.dubbo.common.serialize.Serialization 等,ExtensionFactory.javaSerialization.java 都是纯继承、零额外方法的"标记式"桥接。

3.2 CompatibleInvoker:包装新 Invoker 适配旧签名

Invoker.java 中的内部类 CompatibleInvoker<T> 是桥接的核心组件,其关键行为:

  • invoke(Invocation) 时先判断被包装的新 invoker 本身是否恰好也是旧 Invoker 接口(即"旧实现"):
    • 是:直接调用旧的 invoke(Invocation),并把旧 Result 包成 Result.CompatibleResult(必要时先用 AsyncRpcResult 搬运 value/exception/objectAttachments);
    • 不是:调用新 invoker 的 invoke(invocation.getOriginal()),再用 CompatibleResult 包住返回;
  • getUrl() 返回 new DelegateURL(invoker.getUrl()),即把新的 org.apache.dubbo.common.URL 包装成旧的 com.alibaba.dubbo.common.URL
  • 覆盖 hashCode()/equals() 委托给内部 invoker,保证包装前后在集合、比较中的行为一致。

3.3 DelegateURL:URL 的双向委托

DelegateURL.javacom.alibaba.dubbo.common.URL 的委托子类:

@Deprecated
public class DelegateURL extends com.alibaba.dubbo.common.URL {
    protected final org.apache.dubbo.common.URL apacheUrl;

    public DelegateURL(org.apache.dubbo.common.URL apacheUrl) {
        this.apacheUrl = apacheUrl;
    }

    @Override
    public String getProtocol() {
        return apacheUrl.getProtocol();
    }
    // 其余 get/set 方法同样逐项委托给 apacheUrl
}

它把 getProtocol()setProtocol()getUsername() 等每个方法逐一委托给内部的 org.apache.dubbo.common.URL,并提供静态工厂 DelegateURL.valueOf(String) 一步构造。这样,持有旧 com.alibaba.dubbo.common.URL 类型的旧代码拿到的 URL 数据,实际全部来自新 URL 对象,避免了两个 URL 模型之间的数据拷贝与漂移。

3.4 测试用例如何验证桥接

FilterTest.java 用同一份旧包 MyFilter 验证了两个方向的调用:

  • testDefault:以 org.apache.dubbo.rpc.InvocationRpcInvocation)调用旧 Filter,走 default 桥接方法,断言 res.recreate() 得到 "alibaba"
  • testRecreate:走另一条 recreate 路径,断言 "123test"
  • testInvokeException:以旧 LegacyInvocation 调用,验证旧实现抛出的 RpcException("arg0 illegal")能原样透传给调用方。

RouterTest.java 则验证了新旧 Router 扩展(CompatibleRouterCompatibleRouter2NewRouter)在扩展加载器中混用的正确性。这些测试就是 README 所说"上面那些 API work fine"的证据所在。

四、兼容注解 API:@Service、@Reference 与 @EnableDubbo

4.1 @Service:属性与新 @DubboService 对齐

旧的 Service.java 注解标有 @Deprecated,javadoc 中 @see DubboService 明确推荐使用新注解替代。其属性集与新 API 保持一致,常用的如:

@Deprecated
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
public @interface Service {
    Class<?> interfaceClass() default void.class;
    String interfaceName() default "";
    String version() default "";
    String group() default "";
    String path() default "";
    boolean export() default false;
    boolean dynamic() default true;
    String cluster() default "";
    int timeout() default -1;
    int retries() default -1;
    String loadbalance() default "";
    boolean async() default false;
    int weight() default -1;
    String mock() default "";
    String[] filter() default {};
    // 完整属性见源码
}

注意其中 application() 属性本身又被 @Deprecated 标注并提示 "Do not set it and use the global Application Config"(Service.java),这一点与新版注解的处理一致。

4.2 @EnableDubbo:元注解组合

EnableDubbo.java 的桥接方式与接口类不同——它不继承任何东西,而是作为元注解组合直接复用新框架的实现:

@Deprecated
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {

    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
    boolean multipleConfig() default false;
}

即在旧注解上叠加新的 @EnableDubboConfig@DubboComponentScan,并用 Spring 的 @AliasFor 把旧属性 scanBasePackages / scanBasePackageClasses / multipleConfig 映射到 org.apache.dubbo 包下对应注解的属性。旧代码写 @EnableDubbo(scanBasePackages = "com.example") 后,Spring 容器内实际生效的是新版 DubboComponentScan 的扫描逻辑。相关验证见 EnableDubboTest.javaEnableDubboConfigTest.java,测试工程内的 ProviderConfiguration.javaConsumerConfiguration.java 则演示了新旧注解驱动下 provider/consumer 的实际配置。

五、模块依赖结构:兼容模块依赖什么

dubbo-compatible/pom.xml 说明了该模块在整体工程中的位置:

  • 编译期依赖dubbo-config-spring(注解/Spring 集成能力的来源,@EnableDubbo 桥接所依赖)、dubbo-qos(QoS 控制台命令桥接)、dubbo-remoting-zookeeper-curator5(注册中心)、dubbo-filter-cachedubbo-filter-validation(缓存/校验 Filter 扩展),以及 provided 作用域的 javax.servlet-apijavax.ws.rs-api(支撑 container.page 下的页面桥接类);
  • 测试期依赖dubbo-serialization-hessian2dubbo-serialization-fastjson2dubbo-registry-multicastdubbo-registry-zookeeperdubbo-configcenter-zookeeperdubbo-metadata-report-zookeepercom.alibaba:fastjson 等,覆盖第二节所列单元测试所需的 SPI 实现;
  • 父工程为根 pom.xml 中的 org.apache.dubbo:dubbo-parent,随整个仓库一起构建。

六、使用建议与迁移路线

综合 README 与源码证据,使用该模块时的实践要点:

  1. 仅依赖有测试覆盖的 API 清单(第二节的表)。清单外的 bridge 类(如 remotingcontainer.page 下的部分类)从源码结构看只是继承/委托的骨架,README 明确其"may work with wrong",生产代码不要依赖;
  2. 旧扩展实现可平滑运行:如果你的自定义 Filter/Router/Serialization 等扩展仍实现 com.alibaba.dubbo 旧接口(并注册在 META-INF/dubbo/... SPI 文件中),由于兼容接口继承自新接口,SPI 加载器可直接发现它们——RouterTest.javaExtensionTest.java 验证了新旧扩展混载的场景;
  3. 新扩展一律用新 API 实现:README 将 "Implement your own extensions with new APIs" 标注为 RECOMMENDED。若确有旧 API 缺失或行为异常,官方给出的三条路是:用新 API 自行实现(推荐)、参照 Filter.java 的桥接方式补全兼容实现并向社区贡献、或在 GitHub 提交 issue;
  4. 规划迁移窗口:所有旧 API 均带 @Deprecated,且 README 声明该模块"some day"会被移除。合理的路线是:先接入 dubbo-compatible 保证旧代码可运行 → 逐步把 import 与注解(@Service/@Reference@DubboService/@DubboReference@EnableDubbo@EnableDubboConfig/@DubboComponentScan)切换到 org.apache.dubbo 包 → 移除 dubbo-compatible 依赖。

小结

dubbo-compatible 是 Apache Dubbo 2.7.x 包名迁移期的"减震器":它通过**接口继承 + default 方法翻译 + Compatible 包装类(CompatibleInvoker/CompatibleResult/DelegateURL)**三层机制,让 com.alibaba.dubbo 命名空间下的 Filter、Invoker、Invocation、Result、RpcContext、注解与 URL 等常用 API 无缝落到 org.apache.dubbo 的新实现上。它是有明确边界的过渡设施——README 划定的测试覆盖清单可以放心使用,清单外的 bridge API 应视为不保证行为正确;而所有带 @Deprecated 的旧 API 都只是通往新包名的临时通道,新扩展实现应直接基于 org.apache.dubbo API 编写。

登录后查看全文
热门项目推荐
相关项目推荐