我是靠谱客的博主 微笑书包,最近开发中收集的这篇文章主要介绍手写RPC框架-注解解析、接口代理生成远程服务的核心实现逻辑项目地址,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

手写RPC框架-远程服务调用

  • 远程服务的核心
    • 使用方法
    • 核心点
  • 实现逻辑
    • 1.解析添加指定注解的接口
      • 自定义解析器
      • 拿到要解析的路径
      • 生成代理类 并注入到spring工厂中
        • 主要组件
        • 生成代理类逻辑
          • 主要点
          • 生成代理源码
  • 项目地址

远程服务的核心

使用方法

点击这里查看详细使用方法

// provider为远程服务的服务名称
@WbClient("provider")
public interface TestService {
	// value为想要调用的路径 method为请求的方法,POST GET
    @WbRequestMapping(value = "/test", method = WbRequestMethod.GET)
    void test(@WbRequestParam(value = "asd") String str, String str2);

    @WbRequestMapping(value = "/testbody", method = WbRequestMethod.POST)
    TestUser testpost(@WbRequestBody TestUser testUser);
}

核心点

扫描标注注解的接口,生成实际的代理类,在代理类中实现远程调用(由于在注册中心中已经保存了远程服务的IP和端口号,只需要拿到调用即可)。把生成的代理类注入到spring工厂中,在调用端使用Autowired注解即可自动注入代理类。

实现逻辑

1.解析添加指定注解的接口

自定义解析器

自定义解析类实现ClassPathScanningCandidateComponentProvider接口,可以解析接口。

public class InterfacePathScanningCandidateComponentProvider extends ClassPathScanningCandidateComponentProvider {
    public InterfacePathScanningCandidateComponentProvider(boolean useDefaultFilters) {
        super(useDefaultFilters);
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        AnnotationMetadata metadata = beanDefinition.getMetadata();
        return metadata.isIndependent() && metadata.isInterface();
    }
}

拿到要解析的路径

    @Bean
    public MyBeanDefinitionRegistryPostProcessor myBeanDefinitionRegistryPostProcessor(Environment environment) {
        // 通过配置的wb.packageScan拿到需要解析的路径
        String packagesScan = environment.getProperty("wb.packageScan", String.class, null);
        return new MyBeanDefinitionRegistryPostProcessor(packagesScan);
    }

生成代理类 并注入到spring工厂中

主要组件

通过BeanDefinitionRegistryPostProcessor修改BeanDefinitionRegistry,把代理类注入到spring中。

public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    private String packagesScan;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        // 解析
        Set<BeanDefinition> defs = parseInterfaces();
        for (BeanDefinition def : defs) {
            try {
                Class<?> clazz = forName(def.getBeanClassName());
                // 生成代理类
                Class<?> proxyClass = WbClientProxy.buildProxy(clazz);
                // 代理类注册为RootBeanDefinition后放入Spring中
                RootBeanDefinition beanDefinition = new RootBeanDefinition();
                beanDefinition.setBeanClass(proxyClass);
                beanDefinitionRegistry.registerBeanDefinition(proxyClass.getSimpleName().toLowerCase(), beanDefinition);
            } catch (ClassNotFoundException | NotFoundException | CannotCompileException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * 解析
     *
     * @return
     */
    private Set<BeanDefinition> parseInterfaces() {
        // 解析
        InterfacePathScanningCandidateComponentProvider scanner = new InterfacePathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(WbClient.class));
        return new HashSet<>(scanner.findCandidateComponents(packagesScan));
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    public MyBeanDefinitionRegistryPostProcessor(String packagesScan) {
        this.packagesScan = packagesScan;
    }
}

生成代理类逻辑

主要点
  1. 解析接口注解@WbClient拿到要调用的服务端
  2. 解析接口中的方法注解,拿到请求路径和请求方法
  3. 在代理类中注入注册工厂(IRegister实现类)和WbClientCall调用
  4. 在代理类中方法中获取服务,得到路径,处理响应信息
生成代理源码
public class WbClientProxy {

    /**
     * 生成的代理对象名称前缀
     */
    private static final String PROXY_PREFIX = "WbRpcProxy";

    /**
     * 生成的代理对象名称后缀
     */
    private static final String PROXY_SUFFIX = "Impl";

    /**
     * 参数为@WbRequestBody的类型
     */
    private static final String REQUEST_BODY_PARAMETER = "REQUEST_BODY_PARAMETER";

    public static <T> Class<T> buildProxy(Class<T> t) throws NotFoundException, CannotCompileException, ClassNotFoundException {
        // 获取调用的服务名称
        String clientServerName = t.getAnnotation(WbClient.class).value();
        ClassPool pool = ClassPool.getDefault();
        //创建代理类对象
        CtClass ctClass = pool.makeClass(getImplName(t));
        //设置代理类的接口
        CtClass interj = pool.getCtClass(t.getName());
        // 设置方法的属性
        initClassField(ctClass, interj, pool);
        //代理类的所有方法
        CtMethod[] methods = interj.getDeclaredMethods();
        // 获取所有方法的参数信息
        Map<String, List<String>> parameterMap = buildParametersMap(t);

        for(CtMethod method : methods) {
            // 创建代理类的方法
            CtMethod ctMethod = initClassMethod(method, ctClass, parameterMap, t, clientServerName);
            ctClass.addMethod(ctMethod);
        }
        return (Class) ctClass.toClass();
    }

    private static CtMethod initClassMethod(CtMethod method, CtClass ctClass, Map<String,
            List<String>> parameterMap, Class<?> t, String clientServerName) throws NotFoundException, ClassNotFoundException, CannotCompileException {
        String methodName = method.getName();
        CtMethod cm = new CtMethod(method.getReturnType(), methodName, method.getParameterTypes(), ctClass);
        // 获取参数列表名称
        String methodFullName = buildMethodFullName(method);
        // 该方法参数的映射值
        List<String> parameterNames = parameterMap.get(methodFullName);
        // 解析访问的路径
        WbRequestMapping requestMapping = (WbRequestMapping) method.getAnnotation(WbRequestMapping.class);
        StringBuilder url = new StringBuilder(requestMapping.value());
        boolean containSymbol = false;
        String requestBody = "null";
        if (!CollectionUtils.isEmpty(parameterNames)) {
            String connect = "?";
            for (int i = 0; i < parameterNames.size(); i++) {
                String parameterName = parameterNames.get(i);
                if (StringUtils.EMPTY.equals(parameterName)) {
                    System.err.println(t.getName() + "类中," + method.getName() + "方法的第" + (i + 1) + "参数没有加WbRequestParam注解,无法匹配,请添加WbRequestParam,或在打包时,使用-parameters,可根据参数名自动匹配。");
                    continue;
                }
                if (REQUEST_BODY_PARAMETER.equals(parameterName)) {
                    requestBody = "$" + (i + 1);
                    continue;
                }
                containSymbol = true;
                if ("?".equals(connect)) {
                    url = new StringBuilder(url + connect + parameterName + "="+" + "$" + (i + 1));
                } else {
                    url.append("+"").append(connect).append(parameterName).append("="+").append("$").append(i + 1);
                }
                connect = "&";
            }
        }

        if (!containSymbol) {
            url = new StringBuilder(url + """);
        }
        String returnType = "void".equals(cm.getReturnType().getName()) ? "com.wb.spring.boot.autoconfigure.proxy.Void" : cm.getReturnType().getName();
        String isReturn = "com.wb.spring.boot.autoconfigure.proxy.Void".equals(returnType) ? "" : "return (" + returnType + ")";
        cm.setBody("{" + isReturn + " clientCall.call("http://" + register.getServer("" + clientServerName + "") + "" + url + ","" + requestMapping.method() + "", " + requestBody + ",  Class.forName("" + returnType+ ""));}");

        return cm;
    }

    /**
     * 创建类的属性
     * @param ctClass
     *  生成的代理类
     * @param interj
     *  代理类的接口
     * @param pool
     * @return
     * @throws CannotCompileException
     * @throws NotFoundException
     */
    private static CtClass initClassField(CtClass ctClass, CtClass interj, ClassPool pool) throws CannotCompileException, NotFoundException {
        CtClass[] interfaces = new CtClass[]{interj};
        ctClass.setInterfaces(interfaces);
        // 获取常量池
        ConstPool constPool = ctClass.getClassFile().getConstPool();
        // 创建属性,调用远程服务使用
        CtField ctField  = new CtField(pool.get("com.wb.spring.boot.autoconfigure.proxy.WbClientCall"), "clientCall", ctClass);
        ctField.setModifiers(AccessFlag.PUBLIC);
        FieldInfo fieldInfo = ctField.getFieldInfo();
        // 添加Autowired注解,后续注册到spring中会自动装配
        AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        Annotation filedAnnotation = new Annotation(Autowired.class.getName(), constPool);
        annotationsAttribute.addAnnotation(filedAnnotation);
        fieldInfo.addAttribute(annotationsAttribute);

        CtField registerField  = new CtField(pool.get("com.wb.spring.boot.autoconfigure.register.IRegister"), "register", ctClass);
        ctField.setModifiers(AccessFlag.PUBLIC);
        FieldInfo registerFieldInfo = registerField.getFieldInfo();
        registerFieldInfo.addAttribute(annotationsAttribute);
        // 加入到类文件中
        ctClass.addField(ctField);
        ctClass.addField(registerField);
        return ctClass;
    }

    /**
     * 获取全限定名称
     * @param method
     * @return
     * @throws NotFoundException
     */
    private static String buildMethodFullName(CtMethod method) throws NotFoundException {
        StringBuilder methodFullName = new StringBuilder(method.getName());
        if (method.getParameterTypes() != null) {
            for (CtClass parameterType : method.getParameterTypes()) {
                methodFullName.append(parameterType.getName());
            }
        }

        return methodFullName.toString();
    }

    /**
     * key-方法名称+方法参数类型
     * value-方法参数名称集合
     * @param clazz
     *  要解析的类
     *
     * @return
     *  方法的类对应参数名称
     */
    private static Map<String, List<String>> buildParametersMap(Class<?> clazz) {
        // 同名参数映射使用 此处使用1.8之上的--parameters才能支持,否则只能使用注解映射。
        DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
        Map<String, List<String>> parameterMap = new HashMap<>(8);
        for (Method method : clazz.getMethods()) {
            StringBuilder name = new StringBuilder(method.getName());
            List<String> parameterRequestNames = new ArrayList<>();
            if (method.getParameters() != null) {
                for (Parameter parameter : method.getParameters()) {
                    name.append(parameter.getType().getName());
                    WbRequestParam annotation = parameter.getAnnotation(WbRequestParam.class);
                    String parameterRequestName = StringUtils.EMPTY;
                    if (annotation != null) {
                        parameterRequestName = annotation.value();
                    }
                    WbRequestBody wbRequestBody = parameter.getAnnotation(WbRequestBody.class);
                    if (wbRequestBody != null) {
                        parameterRequestName = REQUEST_BODY_PARAMETER;
                    }
                    parameterRequestNames.add(parameterRequestName);
                }
            }

            String[] parameterNames = discoverer.getParameterNames(method);
            if (parameterNames == null) {
                parameterMap.put(name.toString(), parameterRequestNames);
                continue;
            }
            List<String> correctParameterRequestNames = new ArrayList<>();
            for (int i = 0; i < parameterRequestNames.size(); i++) {
                String parameterRequestName = parameterRequestNames.get(i);
                if (StringUtils.EMPTY.equals(parameterRequestName)) {
                    correctParameterRequestNames.add(parameterNames[i]);
                    continue;
                }
                correctParameterRequestNames.add(parameterRequestName);
            }

            parameterMap.put(name.toString(), correctParameterRequestNames);
        }

        return parameterMap;
    }
    /**
     * 获取代理类的名称
     * @param t
     *  实现类类型
     * @return
     *  实现类名称
     */
    private static <T> String getImplName(Class<T> t) {
        return t.getPackage() + "." + PROXY_PREFIX  + t.getSimpleName() + PROXY_SUFFIX;
    }
}

项目地址

https://gitee.com/xu–wenbin/wb-spring-boot-project

最后

以上就是微笑书包为你收集整理的手写RPC框架-注解解析、接口代理生成远程服务的核心实现逻辑项目地址的全部内容,希望文章能够帮你解决手写RPC框架-注解解析、接口代理生成远程服务的核心实现逻辑项目地址所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部