动态代理
proxy-demo | github.com/liuyjray/gu…
所有的类都会走动态代理吗?
在这个RPC框架中,并不是所有调用都会走动态代理,具体情况如下:
1. 🎯 使用 @RpcReference 注解的情况 - 会走动态代理
当你使用 @RpcReference 注解时,会自动创建动态代理:
@Component
public class HelloController {
@RpcReference(version = "version1", group = "test1")
private HelloService helloService; // 这里会被注入动态代理对象
public void test() {
// 调用这个方法时会走动态代理
String result = helloService.hello(new Hello("111", "222"));
}
}
工作原理:
SpringBeanPostProcessor在Bean初始化后会扫描所有字段- 发现带有
@RpcReference注解的字段时,会:- 创建
RpcClientProxy实例 - 调用
rpcClientProxy.getProxy(declaredField.getType())生成动态代理对象 - 将代理对象注入到字段中
- 创建
2. 🔧 手动创建代理的情况 - 会走动态代理
你也可以手动创建动态代理:
public class SocketClientMain {
public static void main(String[] args) {
RpcRequestTransport rpcRequestTransport = new SocketRpcClient();
RpcServiceConfig rpcServiceConfig = new RpcServiceConfig();
RpcClientProxy rpcClientProxy = new RpcClientProxy(rpcRequestTransport, rpcServiceConfig);
// 手动创建动态代理
HelloService helloService = rpcClientProxy.getProxy(HelloService.class);
// 调用时会走动态代理
String hello = helloService.hello(new Hello("111", "222"));
}
}
3. 🏷️ @RpcService 注解的情况 - 不走动态代理
@RpcService 注解只是用于服务注册,不涉及动态代理:
@RpcService(group = "test1", version = "version1")
public class HelloServiceImpl implements HelloService {
@Override
public String hello(Hello hello) {
// 这里是真实的服务实现,不是代理
return "Hello description is " + hello.getDescription();
}
}
工作原理:
SpringBeanPostProcessor在Bean初始化前会检查类上的@RpcService注解- 如果有该注解,会将服务注册到注册中心(如Zookeeper)
- 但服务本身不是代理对象,而是真实的实现类
4. 📋 总结
| 使用方式 | 是否走动态代理 | 说明 |
|---|---|---|
@RpcReference 注解注入 | ✅ 是 | Spring自动创建并注入代理对象 |
手动创建 RpcClientProxy.getProxy() | ✅ 是 | 手动创建代理对象 |
@RpcService 注解的服务实现 | ❌ 否 | 只是注册服务,本身是真实实现 |
| 直接调用本地实现类 | ❌ 否 | 普通的本地方法调用 |
5. 🔍 动态代理的核心作用
动态代理在RPC框架中的作用是:
- 透明化远程调用:让远程调用看起来像本地调用
- 请求封装:将方法调用封装成
RpcRequest对象 - 网络传输:通过网络发送请求到远程服务
- 结果处理:接收响应并返回给调用方
所以回答你的问题:不是全部都走动态代理,只有客户端调用远程服务时才会走动态代理,服务端的实现类本身不是代理对象。
📚 本地代理 vs RPC代理
1. 本地增强代理 (JdkProxyHandler)
用途:为本地方法调用添加横切关注点(日志、权限、缓存等)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before(method.getName(), args); // 前置增强
Object result = method.invoke(target, args); // 🎯 调用本地目标方法
after(method.getName(), result); // 后置增强
return result;
}
核心特点:
- ✅ 有target对象:真实的本地业务对象
- ✅ 调用method.invoke(target, args):在本地执行真实方法
- ✅ 增强功能:在方法执行前后添加额外逻辑
2. RPC远程代理 (RpcClientProxy)
用途:将本地方法调用转换为远程网络调用
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 1. 构建RPC请求
RpcRequest rpcRequest = RpcRequest.builder()
.methodName(method.getName()) // 只需要方法名
.parameters(args) // 只需要参数
.interfaceName(method.getDeclaringClass().getName())
.paramTypes(method.getParameterTypes())
.requestId(UUID.randomUUID().toString())
.group(rpcServiceConfig.getGroup())
.version(rpcServiceConfig.getVersion())
.build();
// 2. 发送网络请求
RpcResponse<Object> rpcResponse = rpcRequestTransport.sendRpcRequest(rpcRequest);
// 3. 检查响应并返回结果
this.check(rpcResponse, rpcRequest);
return rpcResponse.getData(); // 🎯 不调用method.invoke!
}
核心特点:
- ❌ 无target对象:没有本地业务对象
- ❌ 不调用method.invoke():不在本地执行方法
- ✅ 网络转发:将方法调用信息发送到远程服务器
🔍 核心差异对比
| 特性 | 本地增强代理 | RPC远程代理 |
|---|---|---|
| target对象 | ✅ 有(本地业务对象) | ❌ 无 |
| method.invoke() | ✅ 调用 | ❌ 不调用 |
| 执行位置 | 本地 | 远程服务器 |
| 主要作用 | 增强本地方法 | 远程方法调用 |
| 返回结果 | 本地方法执行结果 | 网络请求响应结果 |
| 错误处理 | 本地异常处理 | 网络异常 + 业务异常 |
💡 关键理解
method.invoke(target, args) 的含义
// method:方法的"说明书"(包含方法名、参数类型、返回类型等)
// target:真正执行方法的对象
// args:方法参数
Object result = method.invoke(target, args);
// ^^^^^^^^^^^^^^^^^^^^^^
// 在target对象上,按照method的描述执行方法
为什么RPC代理不需要method.invoke()?
// RPC场景下:
// 1. 没有本地target对象(业务逻辑在远程服务器)
// 2. 只需要方法的"描述信息"来构建网络请求
// 3. 真正的方法执行发生在远程服务器上
RpcRequest rpcRequest = RpcRequest.builder()
.methodName(method.getName()) // 只要方法名
.parameters(args) // 只要参数
.interfaceName(method.getDeclaringClass().getName()) // 接口名
.paramTypes(method.getParameterTypes()) // 参数类型
.requestId(UUID.randomUUID().toString()) // 请求ID
.build();
🎯 应用场景总结
本地增强代理适用于:
- 🔐 权限控制:方法执行前检查用户权限
- 📝 日志记录:记录方法调用的详细信息
- ⏱️ 性能监控:统计方法执行时间
- 💾 缓存处理:缓存方法执行结果
- 🔄 事务管理:自动开启/提交/回滚事务
- 🛡️ 异常处理:统一异常处理和重试机制
RPC远程代理适用于:
- 🌐 微服务调用:服务间远程方法调用
- 🔗 分布式系统通信:跨进程、跨机器调用
- 📡 透明远程调用:让远程调用像本地调用一样
- 🏗️ 服务治理:负载均衡、熔断、限流等
- 🔍 服务发现:自动发现和调用远程服务
🚀 动态代理的核心价值
1. 统一处理
// 一个invoke方法处理所有方法调用
public Object invoke(Object proxy, Method method, Object[] args) {
// 所有方法都会经过这里,可以统一处理
}
2. 代码复用
// 避免为每个方法写重复的增强逻辑
// 静态代理需要:
// getUserById() { before(); target.getUserById(); after(); }
// saveUser() { before(); target.saveUser(); after(); }
// ...
// 动态代理只需要:
// invoke() { before(); method.invoke(target, args); after(); }
3. 透明性
// 客户端感觉像调用本地方法
UserService userService = rpcClientProxy.getProxy(UserService.class);
String user = userService.getUserById(123L); // 实际是远程调用
4. 灵活性
// 运行时动态决定代理行为
if (method.getName().startsWith("get")) {
// 查询方法的处理逻辑
} else if (method.getName().startsWith("save")) {
// 保存方法的处理逻辑
}
📋 实现模板
本地增强代理模板
public class EnhancementProxy implements InvocationHandler {
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 前置增强
before(method, args);
try {
// 2. 执行目标方法
Object result = method.invoke(target, args);
// 3. 后置增强
after(method, result);
return result;
} catch (Exception e) {
// 4. 异常处理
handleException(method, e);
throw e;
}
}
}
RPC远程代理模板
public class RpcProxy implements InvocationHandler {
private RpcRequestTransport transport;
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 1. 构建RPC请求
RpcRequest request = buildRequest(method, args);
// 2. 发送远程请求
RpcResponse response = transport.sendRequest(request);
// 3. 处理响应
validateResponse(response, request);
// 4. 返回结果
return response.getData();
}
}
🎉 总结
动态代理 = 方法调用的"拦截器" + "转发器"
- 拦截器:拦截所有方法调用,统一处理横切关注点
- 转发器:将方法调用转发到目标对象(本地)或远程服务器(RPC)
动态代理是AOP(面向切面编程)和RPC(远程过程调用)的核心技术基础!