Java 动态代理教程(JDK 动态代理)(以RPC 过程为例)

1. 什么是动态代理

在运行时为指定的接口自动生成代理对象,并通过 invoke 方法增强了这些对象的功能

2. 两个核心组件

java.lang.reflect.Proxy

这个类提供了方法:newProxyInstance()用来创建一个代理对象

java 复制代码
 public static Object newProxyInstance(ClassLoader loader,
                                       Class<?>[] interfaces,
                                       InvocationHandler h)
     throws IllegalArgumentException{
     ......
 }

参数说明:

  1. loader :用于加载代理类的类加载器;通常传入被代理类的类加载器

  2. interfaces : 被代理类实现的一些接口,数组形式;

  3. h : 实现了 InvocationHandler 接口的对象;

疑惑解释

【Q】为什么Loader是用于加载代理类的类加载器,但又传入的是被代理类的类加载器?

【A】由于动态代理类需要实现与被代理类相同的接口 ,那么就要代理类必须具有和被代理类完全相同的接口定义 ,要实现这一点需要代理类使用和被代理类相同的类加载器

java.lang.reflect.InvocationHandler接口(提供了invoke方法)

java 复制代码
public interface InvocationHandler {

    /**
     * 当你使用代理对象调用方法的时候实际会调用到这个方法
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

参数说明:

  1. proxy:动态生成的代理类

  2. method : 与代理类对象调用的方法相对应

  3. args : 当前 method 方法的参数

3. 使用示例(以简单的RPC 过程为例)

定义代理类ClientProxy

说明

  1. getProxy方法中,InvocationHandler h位置(第 3 个入口参数)处,要传实现了InvocationHandler接口的类。由于ClientProxy类实现了这个接口,所以这里在调用newProxyInstance直接传入了this
  2. invoke方法中,没有像别的动态代理示例那样,在其中调用method.invoke(target, args);。因为这里的场景是:在RPC过程中,客户端对输入参数的封装,然后发送给服务端,服务端来执行相应的方法(客户端只有接口定义,而没有接口实现,所以这里并不需要method.invoke(target, args))(但是在服务端中,会调用method.invoke(target, args)
java 复制代码
package com.chanlee.crpc.v1.client;

import com.chanlee.crpc.v1.common.RpcRequest;
import com.chanlee.crpc.v1.common.RpcResponse;
import lombok.AllArgsConstructor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import static com.chanlee.crpc.v1.client.IOClient.sendRequest;

@AllArgsConstructor
public class ClientProxy implements InvocationHandler {
    /**
     * 服务端 IP
     */
    private String host;

    /**
     * 服务端端口号
     */
    private int port;
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //构建request请求
        RpcRequest request = RpcRequest.builder()
                .interfaceName(method.getDeclaringClass().getName())
                .method(method.getName())
                .paramsTypes(method.getParameterTypes())
                .params(args)
                .build();
        //发送请求并获取响应
        RpcResponse<Object> response = sendRequest(host, port, request);
        //返回结果数据
        return response.getData();
    }
    public <T> T getProxy(Class<T> tClass){
        Object o = Proxy.newProxyInstance(
                tClass.getClassLoader(),
                new Class[]{tClass},
                this
        );
        return (T)o;
    }
}

客户端代码(使用上面的代理类)

说明:

  1. 下方代码没有出现Proxy.newProxyInstance(...)是因为ClientProxy类中的getProxy(...)已经封装了该方法
  2. 当代理类调用对应的方法时,会被invoke()方法截取,然后执行invoke()方法中的代码逻辑
java 复制代码
package com.chanlee.crpc.v1.client;

import com.chanlee.crpc.v1.common.User;
import com.chanlee.crpc.v1.service.UserService;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

/**
 * 客户端代码
 */
public class Client{

    public static void main(String[] args){
        ClientProxy clientProxy = new ClientProxy("127.0.0.1", 8003);
        UserService proxy = clientProxy.getProxy(UserService.class);

        //调用方法 1
        User user = proxy.getUserById(1);
        System.out.println(user);
        
        //调用方法 2
        User codingBoy = User.builder()
                .age(25)
                .id(32)
                .realName("coding boy")
                .build();
        Integer i = proxy.insertUser(codingBoy);
        System.out.println(i);
    }

}
相关推荐
程序员黑豆3 小时前
Java包装类:基本类型与对象的桥梁
java·前端·ai编程
mldong7 小时前
"applicant" 契约:退回发起人的闭环设计
java·架构
月华路7 小时前
G1 垃圾回收:脏卡队列、记忆集与并发精化线程机制
java·开发语言
gugucoding8 小时前
47. 【Java】Java内存模型(JMM)与可见性
java·开发语言
zww89491118 小时前
校园跑腿系统开发实战指南:从需求分析到上线部署全流程解析
java
Fluxart.ai9 小时前
电商商品图审核怎么自动化?规则引擎、人工复核与发布门禁
java·前端·自动化
名字还没想好☜9 小时前
Java 线上内存泄漏排查实战:jmap 导堆、MAT 找 GC Roots 与四类常见泄漏
java·开发语言·jvm·内存泄漏
IT爱学堂11 小时前
尚硅谷 - 2025年3月Java+AI大模型应用开发
java·开发语言·人工智能
小贤plus12 小时前
SpringBoot 三大核心注解精讲:@ControllerAdvice、@RestControllerAdvice、@Validated 分组校验(实战)
java
吠品12 小时前
Java byte数组与String互转:编码细节与踩坑记录
java·linux·服务器