RPC使用JDK动态代理

介绍

在 Java 动态代理机制中 InvocationHandler 接口Proxy 类 是核心。
Proxy类 中使用频率最高的方法是**:newProxyInstance()** ,这个方法主要用来生成一个代理对象。

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

这个方法一共有 3 个参数:
1.loader :类加载器,用于加载代理对象。
2.interfaces:被代理类实现的一些接口。
3.h:实现了 InvocationHandler 接口的对象。

要实现动态代理的话,还必须实现 InvocationHandler 来自定义处理逻辑 。当我们的动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现 InvocationHandler 接口类的invoke 方法 来调用。

JDK动态代理类使用步骤

1.定义一个接口及其实现类。

2.自定义 InvocationHandler 并重写 invoke 方法,在 invoke 方法中我们会调用原生方法(被代理类的方法) 并自定义一些处理逻辑。

3 .通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>\[\] interfaces, InvocationHandler h) 方法创建代理对象。

代码示例

1.定义发送短信的接口

java 复制代码
public interface SmsService {
    String send(string message);
}

2.实现发送短信的接口

java 复制代码
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:"+ message);
        return message;
    }
}

3.定义一个JDK动态代理类

java 复制代码
public class DebugInvocationHandler implements InvocationHandler {
    //代理类中的真实对象
    private final Object target;
 
    public DebugInvocationHandler(Object target) {
        this.target = target;
    }
 
    public 0bject invoke(Object proxy, Method method, 0bject[] args) throws InvocationTargetException, IllegalAccessException{
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        Obiect result = method.invoke(target, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return result;
    }
}

invoke() 方法: 当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke()方法,然后 invoke() 方法代替我们去调用了被代理对象的原生方法。

4.获取代理对象的工厂类

java 复制代码
public class JdkProxyFactory{
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
            //目标类的类加载
            target.getclass().getclassLoader();
            //代理需要实现的接口,可指定多个
            target.getClass().getInterfaces()
            //代理对象对应的自定义
            InvocationHandlernew DebugInvocationHandler(target)
        );
    }
}

5.实际使用

java 复制代码
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("java")

在RPC中的使用

RpcClientProxy相当于代理对象的工厂类,同时它还实现了InvocationHandler接口,所以它自己也是JDK动态代理类。

这里的getProxy对应工厂类的getProxy方法。

java 复制代码
public <T> T getProxy(Class<T> clazz) {
    // this代表this.invoke
    return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, this);
}

第一个<T>表示该方法是泛型方法,第二个T是返回值类型,Class<T> clazz是一个类型为<T>的类。this就代表了它自己,因为自己实现了InvocationHandler接口。

这里的invoke对应动态代理类的invoke方法。

java 复制代码
   public Object invoke(Object proxy, Method method, Object[] args) {
        log.info("invoked method: [{}]", method.getName());
        // 根据被调用的方法、方法参数等信息构建一个 RpcRequest 对象,表示一个远程过程调用请求
        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();
        RpcResponse<Object> rpcResponse = null;
        // 判断rpcResponse的类型
        if (rpcRequestTransport instanceof NettyRpcClient) {
            //这里的rpcRequestTransport.sendRpcRequest对应了NettyRpcClient的sendRpcRequest
            CompletableFuture<RpcResponse<Object>> completableFuture = (CompletableFuture<RpcResponse<Object>>) rpcRequestTransport.sendRpcRequest(rpcRequest);
            rpcResponse = completableFuture.get();
        }
        if (rpcRequestTransport instanceof SocketRpcClient) {
            rpcResponse = (RpcResponse<Object>) rpcRequestTransport.sendRpcRequest(rpcRequest);
        }
        this.check(rpcResponse, rpcRequest);
        return rpcResponse.getData();
相关推荐
2601_962071575 小时前
【Java报错已解决】org.springframework.beans.factory.BeanCreationException
java·开发语言
zww89491115 小时前
酒馆预约系统开发实战:从需求分析到上线全流程指南
java·eclipse
淡海水6 小时前
07-04-并发-ConcurrentBag-T-工作窃取WorkStealing算法
开发语言·算法·c#·bag·concurrent·workstealing
CS_Zero6 小时前
C语言sizeof是函数吗?
c语言·开发语言
-今昭-7 小时前
《V2V 迁移实战:将 VMware 虚拟机转为 OpenStack 可用 Glance qcow2 镜像》
开发语言·python
zww89491118 小时前
匿名树洞系统开发实战:从需求分析到部署指南
java·eclipse
黑马程序员毕设9 小时前
基于Java的医院药品管理系统的优化设计与实现
java·开发语言·spring boot·小程序·架构·课程设计·毕设
木井巳9 小时前
【BFS/DFS 解决 FloodFill 算法】太平洋大西洋水流问题
java·算法·leetcode·深度优先·广度优先·宽度优先·推荐算法
白山编程大哥10 小时前
Java 集合算法:从排序、查找到底层原理的实战指南
java·python·算法
_Twink1e10 小时前
openJiuwen 实训营 Day 1 · WorkSwarm 入门教程
开发语言·c++·openjiuwen