JAVA生态的开源项目大量使用反射,并且随处可见注解,如果不懂基础就照葫芦画瓢的确让人很不舒服。
下面我将利用注解和反射,实现一个简单的Web路由(类似spring mvc的感觉)示例:即在处理函数上利用注解配置URI映射,并自动的根据请求的URI调用对应的处理函数。
完整代码: https://github.com/owenliang/java-somewhat/tree/master/src/cc/yuerblog/annotation
package cc.yuerblog.annotation; import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Retention; /** * 用于配置路由的注解,用于类方法 * @author liangdong * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Uri { String path() default "/"; }
通过@interface可以定义注解类Uri。
该注解使用的时候可以接收1个参数叫做path,用于声明哪个方法用于处理哪个URI的请求,稍等下面我们会看到使用示例。
Target元注解声明Uri注解只能用在类的方法上,ElementType也支持指定为类的注解、用于参数的注解、用于私有变量的注解等等。
Retention元注解声明Uri注解是运行时的,也就是说JVM运行我们程序的时候,我们可以通过反射机制拿到Uri注解的详细信息,比如获取其中的path()参数。
还是有点抽象,所以下面先说怎么用Uri。
package cc.yuerblog.annotation; import cc.yuerblog.annotation.Uri; public class App { @Uri(path="/user/{username}/info") public void getUserInfo(String username) { System.out.println("查找用户:" + username); } @Uri(path="/product/{productID}/info") public void getProductInfo(int productID) { System.out.println("查找商品:" + productID); } }
App类相当于Controller,用于处理HTTP请求。
有2个方法:
接下来,我要实现路由逻辑,根据URI找到对应的方法并调用。
package cc.yuerblog.annotation; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.List; import cc.yuerblog.annotation.Uri; public class Main { public static void main(String[] args) { App app = new App(); Main.handleRoute(app, "/user/owenliang/info"); Main.handleRoute(app, "/product/110/info"); } public static void handleRoute(App app, String uri) { // 拆分uri String[] uriArr = uri.split("/"); // 遍历所有方法 Method[] methods = app.getClass().getMethods(); for (Method method : methods) { // 获取Uri注解 Uri anno = method.getAnnotation(Uri.class); if (anno == null) { continue; } // 得到path String path = anno.path(); // 拆分path String[] pathArr = path.split("/"); // 匹配 if (pathArr.length != uriArr.length) { continue; } boolean isMatch = true; ArrayList<String> catchParams = new ArrayList<>(); for (int i = 0; i < pathArr.length; ++i) { if (pathArr[i].startsWith("{")) { // 参数捕获 String paramValue = uriArr[i]; // 捕获参数值 catchParams.add(paramValue); } else if (!pathArr[i].equals(uriArr[i])) { isMatch = false; break; } }
首先创建App对象,测试2个URI的路由结果。
handleRoute负责具体路由逻辑:
// 路由匹配 if (isMatch) { // 进行参数校验 boolean canCall = true; // 反射方法参数 Parameter[] params = method.getParameters(); if (params.length == catchParams.size()) { // 参数数量相等 // 处理每个参数 List prepareParams = new ArrayList(); for (int i = 0; i < params.length; ++i) { // 根据类型进行转换 if (params[i].getType() == int.class) { // 数字参数 prepareParams.add(Integer.parseInt(catchParams.get(i))); } else if (params[i].getType() == String.class) { // 字符串参数 prepareParams.add(catchParams.get(i)); } else { // 不支持的类型 canCall = false; break; } } if (canCall) { // 执行对应的处理函数 try { method.invoke(app, prepareParams.toArray()); } catch (Exception e) { System.out.println(e); } } } break; }
一旦确认path和URI完全匹配,那么就要最终调用该method了。
所以接下来做的事情是:
程序输出:
查找用户:owenliang
查找商品:110
全文结束。
如果文章帮到了你,请您乐于扫码捐赠1元钱,以便支持服务器运转。