为什么dubbo和openFeign都是通过动态代理的方式发起调用
因为它们要实现同一个目标:
让“远程调用”在代码层看起来像“本地方法调用”
而实现这个目标的关键设计就是:动态代理 + 方法拦截
1 一句话本质
Dubbo / OpenFeign = “接口 = 服务入口” 动态代理 = “拦截方法调用 → 转换成远程请求”2 为什么必须用动态代理
如果不用代理,你只能这样写远程调用:
❌ 传统写法(侵入性强)
Dubbo之前(HTTP/Socket手写)
HttpClient client = new HttpClient(); String result = client.post( "http://user-service/user/1", json );问题:
1. 代码和通信强耦合 2. 每个接口都要写一堆模板代码 3. 无法统一治理(重试/负载均衡/降级)❌ 如果不用代理(RPC直连)
UserServiceStub stub = new UserServiceStub(); stub.invoke("queryUser", args);问题:
业务代码必须知道“stub细节” 不符合“面向接口编程”3 动态代理解决什么问题
动态代理的核心作用:
拦截接口方法调用 ↓ 转换成远程请求 ↓ 隐藏网络细节示例(用户代码)
无论 Dubbo 还是 Feign:
UserService userService; userService.queryUser(1L);看起来是:
本地方法调用实际发生了什么
代理对象拦截 ↓ InvocationHandler.invoke() ↓ 组装请求 ↓ 网络通信 ↓ 返回结果4 Dubbo 为什么用动态代理
关键点:RPC必须“透明”
Dubbo希望:
开发者 = 只写接口例如:
@DubboReference UserService userService;实际注入的是:
JDK Proxy / Javassist Proxy拦截点
InvocationHandler.invoke()Dubbo做的事:
1. 获取方法名 2. 获取参数 3. 查找Provider 4. 负载均衡 5. 序列化 6. Netty发送核心思想
接口 = 服务契约 代理 = 网络转换器5 OpenFeign 为什么用动态代理
Feign的设计更简单:
HTTP接口 = 方法映射例如:
@FeignClient("user-service") public interface UserClient { @GetMapping("/user/{id}") User get(@PathVariable Long id); }Feign代理做什么
调用:
userClient.get(1L);进入代理:
MethodInterceptor转换为:
GET /user/1然后:
HttpClient / OkHttp6 为什么“必须是接口 + 动态代理”
原因1:无法修改业务代码
如果不用代理:
UserClientImpl client = new UserClientImpl();那你就变成:
强依赖实现类原因2:必须解耦网络逻辑
业务只应该关心:
方法名 + 参数不应该关心:
HTTP / TCP / JSON / 序列化原因3:统一做横切能力
动态代理可以统一插入:
Dubbo
负载均衡 容错 重试 路由 熔断Feign(Spring Cloud)
Ribbon / LoadBalancer Retry CircuitBreaker Interceptor7 本质对比(核心)
Dubbo Proxy: 方法 → RPC协议 → Netty Feign Proxy: 方法 → HTTP请求 → REST API8 如果不用动态代理会怎样
你会变成:
每个服务手写 HTTP/RPC代码 无法统一治理 无法做到接口透明 代码严重重复9 面试标准总结
可以这样回答:
Dubbo 和 OpenFeign 都采用动态代理的原因,是为了实现“接口即服务”的编程模型,使远程调用对开发者透明。
动态代理可以在不修改业务代码的情况下拦截接口方法调用,在运行时统一转换为 RPC 或 HTTP 请求,从而屏蔽网络通信细节。
同时代理层还能统一增强能力,例如负载均衡、重试、熔断、路由和监控等横切逻辑。
本质上:
- Dubbo 是将方法调用转为 RPC 调用
- Feign 是将方法调用转为 HTTP 请求
一句话记忆
动态代理 = 把“方法调用”变成“远程调用”的翻译器