接上篇。
服务端如何将某些指定的方法暴露出去,比如服务端有:
类A,方法A1()
类B,方法B1()、B2()
类C,方法C1()
我们想把类中的方法暴露一部分出来,供客户端调用,如:
类A
:暴露,方法A1()
:暴露
类B
:暴露,方法B1()
:暴露,方法B2():不暴露
类C:不暴露,方法C1()
怎么才能做到动态方便的进行设置?
这种情况下,注解方式则派上了用场。
注解定义
我们编写2个注解,1个为类注解,表示该类暴露给客户端;另一个为方法注解,表示该方法暴露给客户端。
类级别的注解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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;
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE ) public @interface RpcClazz { }
JAVA
|
方法级别的注解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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;
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD ) public @interface RpcMethod { }
JAVA
|
注解解析
具体步骤为:
1、传入包名,得到all业务类,得到文件名
2、通过反射得到类
3、解析类是否有注解
4、解析方法是否有注解
5、发布
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| 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;
public class AnnoUtil {
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(".", "/")); 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){ paras.add(paraType.getSimpleName()); } methodReturn.put(method.getName(),paras); methodsReturn.add(methodReturn); } } map.put(packageName+"."+ name,methodsReturn); } } return map; } }
JAVA
|
编写几个类和方法,加入我们的注解来测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.leehao.rpc.server.dao;
import com.leehao.rpc.anno.RpcClazz; import com.leehao.rpc.anno.RpcMethod;
@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); } }
JAVA
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.leehao.rpc.server.dao;
import com.leehao.rpc.anno.RpcClazz; import com.leehao.rpc.anno.RpcMethod;
@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); }
}
JAVA
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.leehao.rpc.server.dao;
import com.leehao.rpc.anno.RpcClazz; import com.leehao.rpc.anno.RpcMethod;
public class OrgDao { @RpcMethod public void test(){ System.out.println("name"); }
}
JAVA
|
预期
- OrderDao类有注解,query、query1、query2方法有注解:
所以query、query1、query2暴露,query3不暴露出来。
- UserDao类有注解,sayHello、sayBye有注解:
所以2个均暴露出来。
- OrgDao类没有注解,所以下面的方法无论是否加注解,均无法暴露。
测试
在这里我们采用SpringBoot作为web容器,调用上述的接口,来将结果显示到页面上,如对springboot了解不够,可自行学习,默认大家都了解Maven、springboot等相关知识。
pom中引入依赖:
1 2 3 4 5 6 7 8
| <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>
XML
|
编写Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 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;
@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); } }
JAVA
|
启动类启动
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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); }
}
JAVA
|
在浏览器地址栏输入(端口号根据实际情况而定):
http://localhost:8765/getInstance?packageName=com.leehao.rpc.server.dao
即可发现已暴露的所有接口方法:

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