前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >手写RPC通信和服务发现——模拟Dubbo的实现机制(下篇)

手写RPC通信和服务发现——模拟Dubbo的实现机制(下篇)

作者头像
leehao
发布于 2025-02-11 02:31:00
发布于 2025-02-11 02:31:00
5600
代码可运行
举报
文章被收录于专栏:leehaoleehao
运行总次数:0
代码可运行

服务端如何将某些指定的方法暴露出去,比如服务端有:

类A,方法A1()

类B,方法B1(),方法B2()

类C,方法C1()

我们想把类中的方法暴露一部分出来,供客户端调用,如:

类A :暴露,方法A1():暴露

类B:暴露,方法B1():暴露,方法B2():不暴露

类C:不暴露,方法C1()

怎么才能做到动态方便的进行设置?

这种情况下,注解方式则派上了用场。

注解定义

我们编写2个注解,1个为类注解,表示该类暴露给客户端;另一个为方法注解,表示该方法暴露给客户端。

类级别的注解:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.leehao.rpc.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @ClassName RpcClazz
 * @Description RpcClazz
 * @Author lihao
 * @Date 2019/9/25 14:58
 * @Version 1.0
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE )
public @interface RpcClazz {
}

方法级别的注解:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.leehao.rpc.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @ClassName RpcClazz
 * @Description RpcClazz
 * @Author lihao
 * @Date 2019/9/25 14:58
 * @Version 1.0
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD )
public @interface RpcMethod {
}

注解解析

具体步骤为:

1、传入包名,得到all业务类,得到文件名

2、通过反射得到类

3、解析类是否有注解

4、解析方法是否有注解 *

5、发布

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.leehao.rpc.utils;

import com.leehao.rpc.anno.RpcClazz;
import com.leehao.rpc.anno.RpcMethod;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName AnnoUtil
 * @Description AnnoUtil
 * @Author lihao
 * @Date 2019/9/25 15:02
 * @Version 1.0
 **/
public class AnnoUtil {


    //传入包名packageName
    //返回多个类 Map表示
    //一个类、多个方法
    //一个方法,多个参数
    //所以最后返回结果为:Map<String, List<Map<String,List<String>>>> 
    public static Map<String, List<Map<String,List<String>>>>  parseAnno(String packageName) throws Exception {
        Map<String, List<Map<String,List<String>>>> map = new HashMap<>();
        //获取项目跟路径
        String basePath = AnnoUtil.class.getResource("/").getPath();
        //获取当前包所在的路径,将.替换为/
        File file = new File(basePath + packageName.replace(".", "/"));
        //获取包路径下所有的文件(严格讲,需要获取.class后缀的文件)
        String[] names = file.list();
        for (String name : names) {
            //去掉后缀,拿到类名
            name = name.replaceAll(".class", "");
            //通过反射获取该类
            Class<?> clazz = Class.forName(packageName + "." + name);
            //是否有类级别的注解
            if (clazz.isAnnotationPresent(RpcClazz.class)) {
                List<Map<String,List<String>>> methodsReturn = new ArrayList<>();
                //通过反射获取所有方法,包括私有方法
                Method[] methods = clazz.getDeclaredMethods();
                for (Method method : methods) {
                    //判断方法是否有注解
                    if (method.isAnnotationPresent(RpcMethod.class)){
                        Map<String,List<String>> methodReturn = new HashMap<>();
                        List<String> paras = new ArrayList<>();
                        //遍历方法类型
                        Class<?>[] paraTypes = method.getParameterTypes();
                        for (Class<?> paraType:paraTypes){
                            //将变量类型加入list中
                            paras.add(paraType.getSimpleName());
                        }
                        //将该方法中所有的参数类型加入map中,其中key为方法名
                        methodReturn.put(method.getName(),paras);
                        //将方法都加入到list中去
                        methodsReturn.add(methodReturn);
                    }
                        
                }
                //将该类下的所有暴露方法都加入到map中,其中key为包全名
                map.put(packageName+"."+ name,methodsReturn);
            }
        }
        //返回最终结果
        return map;
    }
}

编写几个类和方法,加入我们的注解来测试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.leehao.rpc.server.dao;

import com.leehao.rpc.anno.RpcClazz;
import com.leehao.rpc.anno.RpcMethod;

/**
 * @ClassName OrderDao
 * @Description OrderDao
 * @Author lihao
 * @Date 2019/9/24 18:58
 * @Version 1.0
 **/
@RpcClazz
public class OrderDao {
    @RpcMethod
    public void query(String name){
        System.out.println("name="+name);
    }
    @RpcMethod
    public void query1(String name1,String name2){
        System.out.println("name="+name1+name2);
    }
    @RpcMethod
    public void query2(int name){
        System.out.println("name="+name);
    }

    public void query3(int name){
        System.out.println("name="+name);
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.leehao.rpc.server.dao;

import com.leehao.rpc.anno.RpcClazz;
import com.leehao.rpc.anno.RpcMethod;

/**
 * @ClassName UserDao
 * @Description UserDao
 * @Author lihao
 * @Date 2019/9/24 18:58
 * @Version 1.0
 **/
@RpcClazz
public class UserDao {
    @RpcMethod
    public void sayHello(String name){
        System.out.println("name="+name);
    }
    @RpcMethod
    public void sayBye(String name1,String name2){
        System.out.println("name="+name1+name2);
    }

}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.leehao.rpc.server.dao;

import com.leehao.rpc.anno.RpcClazz;
import com.leehao.rpc.anno.RpcMethod;

/**
 * @ClassName UserDao
 * @Description UserDao
 * @Author lihao
 * @Date 2019/9/24 18:58
 * @Version 1.0
 **/
public class OrgDao {
    @RpcMethod
    public void test(){
        System.out.println("name");
    }

}

预期:

OrderDao类有注解,query、query1、query2方法有注解:

所以query、query1、query2暴露,query3不暴露出来

UserDao类有注解,sayHello、sayBye有注解,

所以2个均暴露出来

OrgDao类没有注解,所以下面的方法无论是否加注解,均无法暴露。

测试

在这里我们采用SpringBoot作为web容器,调用上述的接口,来将结果显示到页面上,如对springboot了解不够,可自行学习,默认大家都了解Maven、springboot等相关知识。

pom中引入依赖:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
</dependency>

编写Controller

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.leehao.rpc.controller;

import com.leehao.rpc.utils.AnnoUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;

/**
 * @ClassName RegistryController
 * @Description RegistryController
 * @Author lihao
 * @Date 2019/9/25 15:36
 * @Version 1.0
 **/
@Controller
@RequestMapping("/")
public class RegistryController {

    @ResponseBody
    @GetMapping("/getInstance")
    public Map<String, List<Map<String, List<String>>>> getInstance(@RequestParam("packageName") String packageName) throws Exception {
        return AnnoUtil.parseAnno(packageName);
    }
}

启动类启动:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.leehao.rpc;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RpcServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(RpcServerApplication.class, args);
    }

}

在浏览器地址栏输入(端口号根据实际情况而定):http://localhost:8765/getInstance?packageName=com.leehao.rpc.server.dao

即可发现已暴露的所有接口方法:

和我们的预期是一致的,至此服务注册和发现已完成。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-02-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档