guide-rpc-framework笔记(三):动态代理

36 阅读6分钟

动态代理

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 注解的字段时,会:
    1. 创建 RpcClientProxy 实例
    2. 调用 rpcClientProxy.getProxy(declaredField.getType()) 生成动态代理对象
    3. 将代理对象注入到字段中

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框架中的作用是:

  1. 透明化远程调用:让远程调用看起来像本地调用
  2. 请求封装:将方法调用封装成 RpcRequest 对象
  3. 网络传输:通过网络发送请求到远程服务
  4. 结果处理:接收响应并返回给调用方

所以回答你的问题:不是全部都走动态代理,只有客户端调用远程服务时才会走动态代理,服务端的实现类本身不是代理对象

📚 本地代理 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(远程过程调用)的核心技术基础!