手撸RPC框架 动态代理屏蔽RPC服务调用底层细节

2023年 7月 14日 42.6k 0

大家好,我是小趴菜,接下来我会从0到1手写一个RPC框架,该专题包括以下专题,有兴趣的小伙伴就跟着我一起学习吧

本章源码地址:gitee.com/baojh123/se…

自定义注解 -> opt-01
服务提供者收发消息基础实现 -> opt-01
自定义网络传输协议的实现 -> opt-02
自定义编解码实现 -> opt-03
服务提供者调用真实方法实现 -> opt-04
完善服务消费者发送消息基础功能 -> opt-05
注册中心基础功能实现 -> opt-06
服务提供者整合注册中心 -> opt-07
服务消费者整合注册中心 -> opt-08
完善服务消费者接收响应结果 -> opt-09
服务消费者,服务提供者整合SpringBoot -> opt-10
动态代理屏蔽RPC服务调用底层细节 -> opt-10
SPI机制基础功能实现 -> opt-11
SPI机制扩展随机负载均衡策略 -> opt-12
SPI机制扩展轮询负载均衡策略 -> opt-13
SPI机制扩展JDK序列化 -> opt-14
SPI机制扩展JSON序列化 -> opt-15
SPI机制扩展protustuff序列化 -> opt-16

前言

在上一章中,我们已经成功的将服务提供者和服务消费者与SpringBoot成功整合了。但是在那一章中,我们已经实现了基于动态代理来屏蔽了RPC顶层服务调用的细节,这一章来说明如何去实现动态代理

实现

image.png

新建 xpc-rpc-proxy 代理模块,然后在创建二个子模块

  • com.xpc.proxy.common.ObjectProxy
package com.xpc.proxy.common;



import com.xpc.proxy.common.consumer.Consumer;
import com.xpc.rpc.common.enums.RpcMsgType;
import com.xpc.rpc.protocol.ProtocolMessage;
import com.xpc.rpc.protocol.header.RpcHeader;
import com.xpc.rpc.protocol.request.RpcRequest;

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

public class ObjectProxy  implements InvocationHandler {


    private Consumer consumer;

    private Class clazz;

    public ObjectProxy(Consumer consumer,Class clazz) {
        this.consumer = consumer;
        this.clazz = clazz;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        
        //开始构造请求对象
        ProtocolMessage rpcRequestProtocolMessage = new ProtocolMessage();
        RpcRequest rpcRequest = new RpcRequest();
        RpcHeader rpcHeader = new RpcHeader();
        rpcHeader.setMsgType(RpcMsgType.REQUEST.getType());
        rpcHeader.setRequestId(1L);
        rpcRequestProtocolMessage.setRpcHeader(rpcHeader);
        rpcRequest.setClassName(clazz.getName());
        rpcRequest.setMethodName(method.getName());
        rpcRequest.setParameterTypes(method.getParameterTypes());
        rpcRequest.setParameters(args);
        rpcRequestProtocolMessage.setT(rpcRequest);
        
        //发送请求
        return consumer.sendRequest(rpcRequestProtocolMessage);
    }
}
  • Consumer:客户端发送请求的接口,RpcConsumer是它的具体实现
  • Class:代理接口的class,我们是给接口直接进行代理,所以这个Class也就是这个被代理的
    接口的class

创建获取代理的通用接口:com.xpc.proxy.common.ProxyFactory

package com.xpc.proxy.common;

import com.xpc.proxy.common.consumer.Consumer;

public interface ProxyFactory {

    /**
     * 获取代理对象
     */
     T getProxy(Class clazz);

}

JDK动态代理实现: com.xpc.rpc.proxy.jdk.JdkProxy

package com.xpc.rpc.proxy.jdk;

import com.xpc.proxy.common.ObjectProxy;
import com.xpc.proxy.common.ProxyFactory;
import com.xpc.proxy.common.consumer.Consumer;

import java.lang.reflect.Proxy;

public class JdkProxy implements ProxyFactory {


    private Consumer consumer;

    public JdkProxy(Consumer consumer) {
        this.consumer = consumer;
    }
    
    //获取代理对象
    @Override
    public   T getProxy(Class clazz) {
        ClassLoader classLoader = clazz.getClassLoader();
        Class[] interfaces = new Class[]{clazz};
        return (T) Proxy.newProxyInstance(classLoader, interfaces, new ObjectProxy(consumer,clazz));
    }

}

在 com.xpc.consumer.spring.scanner.reference.ReferenceScanner中,我们扫描到有成员变量被@DubboReference注解标记,那么我们就要为这个接口生成代理对象,并注入到这个变量中去

package com.xpc.consumer.spring.scanner.reference;

import com.xpc.consumer.spring.config.RpcConsumerAutoConfig;
import com.xpc.consumer.spring.scanner.ClassScanner;
import com.xpc.rpc.annotation.DubboReference;
import com.xpc.rpc.consumer.RpcConsumer;
import com.xpc.rpc.proxy.jdk.JdkProxy;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.List;

public class ReferenceScanner extends ClassScanner {

    private static final Logger LOGGER = LoggerFactory.getLogger(ReferenceScanner.class);

    public void doScanDubboReferenceByPackage(String packageName) throws Exception{
        // 其它代码省略
        classList.forEach(item -> {
            try {
                Class clazz = Class.forName(item);
                Field[] clazzFields = clazz.getDeclaredFields();
                for(Field field : clazzFields) {
                    DubboReference dubboReference = field.getAnnotation(DubboReference.class);
                    if(dubboReference != null) {
                        Class targetClazz = field.getType();
                        
                        JdkProxy jdkProxy = new JdkProxy(RpcConsumer.getInstance());
                        
                        //获取这个接口的代理对象
                        Object proxy = jdkProxy.getProxy(targetClazz);
                        
                        //将代理对象设置到这个变量中去
                        setField(field, RpcConsumerAutoConfig.getObject(clazz),proxy,true);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
    /**
     * 设置类的属性值
     *
     * @param field 成员变量
     * @param target 类实例
     * @param value  成员变量的值
     * @param accessible 是否允许设置私有属性
     */
    public static void setField(Field field, Object target, Object value,boolean accessible){
        try {
            //是否允许设置私有的成员变量的值
            field.setAccessible(accessible);
            field.set(target,value);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

}

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论