【手写RPC框架(二)】加入动态代理

本文详细介绍了如何使用Java动态代理实现RPC远程调用。通过序列化调用信息,服务端接收到请求后执行相应方法并返回结果。客户端通过代理类封装调用信息并发送给服务端,然后接收并解析服务端的响应。这种方式减少了代码冗余,实现了AOP的拦截功能。
摘要由CSDN通过智能技术生成

一、为什么加入动态代理

\quad\quad 如果没有动态代理,远程调用时,需要对每个类都要建立代理,这样会导致代码十分冗余,我们通过Java中Proxy,动态的构建类,来实现AOP的功能。

二、加入动态代理实现流程

2.1 服务端

2.1.1 调用信息序列化

\quad\quad 因为需要通过反射的方式执行方法,因此我们需要将函数的全部执行信息都要进行序列化,以便进行二进制传输。

public class RpcRequest implements Serializable {

    private static final long serialVersionUID = 6011503509272346423L;
    //类名
    public String className;
    //方法名
    private String methodName;
    //参数类型
    private Class<?>[] parameterTypes;
    //参数
    private Object[] arguments;

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Class<?>[] getParameterTypes() {
        return parameterTypes;
    }

    public void setParameterTypes(Class<?>[] parameterTypes) {
        this.parameterTypes = parameterTypes;
    }

    public Object[] getArguments() {
        return arguments;
    }

    public void setArguments(Object[] arguments) {
        this.arguments = arguments;
    }

    public RpcRequest() {
    }

    public RpcRequest(String className, String methodName, Class<?>[] parameterTypes, Object[] arguments) {
        this.className = className;
        this.methodName = methodName;
        this.parameterTypes = parameterTypes;
        this.arguments = arguments;
    }
}
2.1.2 服务端逻辑

\quad\quad 连接socket,接收客户端传递的数据,并执行相应方法,并将计算结果传递给客户端。

public class serverDPC {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8889);
        try{
            //等待连接
            Socket accept = serverSocket.accept();
            //建立连接后,从socket中获取输入流
            ObjectInputStream objectInputStream = new ObjectInputStream(accept.getInputStream());
            //从输入流中读取对象
            RpcRequest rpcRequest=(RpcRequest)objectInputStream.readObject();
            // 读取类名
            String classFullName=rpcRequest.getClassName();
            // 读取方法名
            String methodName=rpcRequest.getMethodName();
            // 读取方法入参类型
            Class<?>[] parameterTypes=rpcRequest.getParameterTypes();
            // 读取方法调用入参
            Object[] parameters=rpcRequest.getArguments();
            //因为readObject()从输入流获取的对象无法实例化(此时只是字符意思),故需要反射机制
            //获取反射机制类
            Class<?> aClass = Class.forName(classFullName);
            Method method = aClass.getMethod(methodName, parameterTypes);
            //通过反射执行方法
            Object invoke = method.invoke(aClass.newInstance(), parameters);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(accept.getOutputStream());
            objectOutputStream.writeObject(invoke);
        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

2.2 客户端

2.2.1 客户端代理类

\quad\quad 对于InvocationHandler,相当于AOP中的拦截器,所有调用代理类的方法都会被拦截下来转到InvocationHandler中的invoke中执行。因此,我们在Consumer实现时,需要在invoke方法中,将类的调用信息(类名,方法名、参数等)序列化后打包,通过socket发送给服务端来执行远程过程调用。

\quad\quad 编写计算器代理类逻辑

  • 以引用的方式指向被代理类
  • 在invoke方法中建立socket连接
  • 将被代理类的调用信息(类名、方法名、参数)序列化后传递给服务端
  • 接收服务端返回的计算结果
public class ConsumerProxy implements InvocationHandler {
    //被代理类
    private final Class<?> serviceClass;

    public ConsumerProxy(Class<?> serviceClass) {
        this.serviceClass = serviceClass;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Socket socket = new Socket("127.0.0.1", 8889);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        //数据打包
        RpcRequest rpcRequest = new RpcRequest(serviceClass.getName(), method.getName(), method.getParameterTypes(), args);
        //通过socket发送数据
        objectOutputStream.writeObject(rpcRequest);
        //接收返回数据
        ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
        return inputStream.readObject();
    }
}

2.3 通过RPC远程调用

  • 创建代理类的InvocationHandler,相当于AOP拦截器,使得调用代理类的方法都会转入invoke中执行。
  • 创建代理类(调用方法已经在invoke中完成)。
  • 调用相应的方法。
public class ClientDPC {

    public static void main(String[] args) {
        //创建代理类的InvocationHandler
        ConsumerProxy proxy=new ConsumerProxy(CalculatorImpl.class);
        //创建代理类
        //代理类会声明被代理类的接口
        Calculator cal=(Calculator) Proxy.newProxyInstance(Calculator.class.getClassLoader(),new Class<?>[]{Calculator.class},proxy);
        //调用
        int a=cal.add(5,1);
        System.out.println("RPC CONNECTED!"+a);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值