使用 JDK 动态代理的核心是InvocationHandler接口和Proxy类。

  • InvocationHandler 接口
    实现该接口来处理自定义逻辑,当动态代理对象调用一个方法时,这个方法的调用就会被转发到实现了 InvocationHandler 接口类的invoke方法来调用。
    1
    2
    3
    4
    5
    public interface InvocationHandler {
    /**
    * 当你使用代理对象调用方法的时候实际会调用到这个方法
    */
    public Object invoke(Object proxy, Method method, Object[] args)
  • Proxy 类
    使用最多的是 Proxy 类的newProxyInstance()方法,用来生成代理对象。
    1
    2
    3
    public static Object newProxyInstance(ClassLoader loader,
    Class<?>[] interfaces,
    InvocationHandler h) {}

生成代理对象方法的三个参数分别是:
loader:类加载器,用于加载代理对象;
interfaces:被代理类实现的接口;
h:实现了 InvocationHandler 接口的对象。

总的来说,JDK 动态代理的使用步骤:

  1. 定义一个接口及实现类;
  2. 自定义 InvocationHandler 并重写 invoke 方法,在 invoke 方法中通常会调用原生方法并自定义一些处理逻辑;
  3. 通过 Proxy.newProxyInstance() 方法创建代理对象。

在 RPC 中,是如何使用 JDK 动态代理的呢?

定义 JDK 动态代理类,实现核心的 InvocationHandler 接口,重写 invoke 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ClientProxy implements InvocationHandler {
// 传入参数service接口的class对象,反射封装成一个request
private String host;
private int port;

// jdk动态代理,每一次代理对象调用方法,都会经过此方法增强(反射获取request对象,socket发送到服务端)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 通过建造者模式构建RPC请求
RpcRequest request = RpcRequest.builder()
.interfaceName(method.getDeclaringClass().getName())
.methodName(method.getName())
.params(args)
.paramsType(method.getParameterTypes())
.build();
// IOClient.sendRequest 和服务端进行数据传输
RpcResponse response = IOClient.sendRequest(host, port, request);
return response.getData();
}

获取代理对象的工厂类,这里我将工厂类的getProxy()方法定义在动态代理类当中。

1
2
3
4
5
// 创建代理对象
public <T> T getProxy(Class<T> clazz) {
Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
return (T) o;
}

其他的,在原生实现类和对应的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class UserServiceImpl implements UserService {
@Override
public User getUserByUserId(Integer id) {
System.out.println("客户端查询了" + id + "的用户");
// 模拟从数据库中取用户的行为
Random random = new Random();
User user = User.builder()
.userName(UUID.randomUUID().toString())
.id(id)
.sex(random.nextBoolean()).build();
return user;
}

@Override
public Integer insertUserId(User user) {
System.out.println("插入数据成功" + user.getUserName());
return user.getId();
}
}