上篇博客中,咱们创建了一个TransServlet类,它被用来处理系统中全部的请求,也即,TransServlet是程序的入口。同时咱们也在TransServlet中访问了DemoC类中的helloString()方法,可是访问方式是直接硬编码的,本篇咱们将会把这些硬编码的代码修改为动态的,请不要忘记在第六篇博客中咱们创建了一个注解C,还不要忘记DemoC类上有注解C,这们就要从这些注解入手了。同时上篇遗留的一个问题就是在地址栏输入java
http://127.0.0.1:2016/webby/demo/helloString.ts?hello=helloc
后,程序能判断出目标类名及具体方法就行了,这也是本篇博客的目的。git
其实在TransServlet类中咱们将要按顺序作以下功能:web
还记得service()方法的内容吧,为了说明,再贴一下。数组
@Override protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // System.out.print(System.currentTimeMillis()); DemoC demoC = new DemoC(); demoC.helloString("这是DemoC"); }
咱们要作的第一步就是获取请求字符串。HttpServletRequest给咱们提供了不少方法来获取请求信息。如浏览器
request.getParameter(); request.getServletContext(); request.getSession(); request.getRequestURI(); request.getRequestURL(); request.getAttribute(); ......
对,你没有看错!request.getRequestURI()和request.getRequestURL()就是获取请求字符串的,假如请求路径是http://127.0.0.1:2016/webby/demo/helloString.ts
,二者的差异以下:安全
request.getRequestURI()的值是/webby/demo/helloString.ts request.getRequestURL()的值是http://127.0.0.1:2016/webby/demo/helloString.ts
此时URI是http://127.0.0.1:2016/webby/demo/helloString.ts ,咱们要把demo/helloString.ts解析出来,request.getContextPath()获得的结果是http://127.0.0.1:2016/webby ,二者相处理便可得出尾部字符串demo/helloString.ts,哈,结果快出来了,好高兴啊。ide
从请求路径中咱们已经获取到了demo/helloString,其中前者是控制层类,后者是指定的方法名。此时咱们须要一个工具类来保存控制层名称与类的对应关系,这个类要在项目启动的时候咱们要收集全部被注解C标记的类,将这些类存放以便之后使用。工具
获取目标控制层类就显得方便了,在工具类中提供对应的获取方法就行。测试
上一步咱们获得了方法名。咱们要获得目标控制层类的指定方法。java.lang.reflect
包下给咱们提供了动态调用类对象方法的方式。以下提供了动态调用的测试代码:编码
[@Test](http://my.oschina.net/azibug) public void testCallMethodInDemoC() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { DemoC demoC = new DemoC(); Class<?> cls = demoC.getClass(); Method m = cls.getDeclaredMethod("helloString", String.class); m.invoke(demoC, "haha"); }
咱们能够看到打印出了
param hello: haha
说明,咱们确实动态调用了demoC对象的helloString()方法,而且以"haha"为参数。
下面说说具体的实现。
比较重要的是第二步获取目标控制层类
,这须要咱们添加一个工具类,它的全限定名为com.billy.jee.framework.datatrans.CNameAndClassPairs
,它须要提供一个对外的add方法来添加像DemoC这样的控制层类,还须要提供一个对外的get方法来根据名称获取类对象。它的代码大体以下(省略注释代码):
package com.billy.jee.framework.datatrans; import java.util.HashMap; import java.util.Map; public final class CNameAndClassPairs { private CNameAndClassPairs() {} private static Map<String, Class<?>> pathAndCPairs = new HashMap<>(); public static void add(final String path, final Class<?> classs) { CNameAndClassPairs.pathAndCPairs.put(path, classs); } public static Class<?> get(final String path) { return CNameAndClassPairs.pathAndCPairs.get(path); } }
须要说明的是,该类没有线程安全机制。但不是这里要说明的重点。此时咱们要往CNameAndClassPairs里填充数据,正常的做法应该是在项目启动时检查全部被C注解标记的类,并将它们存储在CNameAndClassPairs,但此处为了方便演示,咱们使用简单的静态块来实现。在CNameAndClassPairs类中添加以下静态代码块:
static { Class<?> cls = DemoC.class; C c = cls.getAnnotation(C.class); String value = c.value(); CNameAndClassPairs.pathAndCPairs.put(value, cls); }
同时TransServlet类的service方法也有了相应的修改以下:
@Override protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // System.out.print(System.currentTimeMillis()); //DemoC demoC = new DemoC(); //demoC.helloString("这是DemoC"); String requestURI = req.getRequestURI(); String contextPath = req.getContextPath() + "/"; if (requestURI.endsWith(REQUEST_SUFFIX)) { requestURI = requestURI.substring(contextPath.length()); requestURI = requestURI.substring(0, requestURI.length() - REQUEST_SUFFIX.length()); String reqClass = requestURI; String reqMethod = ""; if (requestURI.indexOf("/") > -1) { reqClass = requestURI.substring(0, requestURI.indexOf("/")); reqMethod = requestURI.substring(requestURI.indexOf("/") + 1); } else { System.out.println("没有方法名,请检查"); return; } Class<?> clazz = CNameAndClassPairs.get(reqClass); Object obj = null; try { obj = clazz.newInstance(); Method[] methods = clazz.getDeclaredMethods(); Method m = null; for (Method method : methods) { String methodname = method.getName(); if (methodname.equals(reqMethod)) { m = method; Object[] params = new Object[] {"aa"}; m.invoke(obj, params); break; } } } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } else { System.out.println("请求路径不以.ts结尾,不会走到此处,由于已在we.xml中配置过了。"); } }
浏览器中输入
http://127.0.0.1:2016/webby/demo/helloString.ts?name=sdf
请求一下试试吧。
须要注意的是以下代码
Object[] params = new Object[] {"aa"}; m.invoke(obj, params);
咱们在此处添加了一个params数组用于拼凑在请求helloString()方法时的参数,由于不给参数的话,会有错误出现,具体什么错误,各位请本身尝试吧。其实咱们一开始测试例子的时候应该用一个无参的方法,哎……上面的代码还有个问题,就是invoke的返回值问题,一个请求到达控制层后必定会有返回的,或是跳转页面,或是跳到另外一个控制层,或是一个Ajax请求等。
此处咱们又遗留了两个问题:
说一下,该系列博客的代码已托管在git.oschina.net里。具体连接是http://git.oschina.net/leaflife/do-c-for-blog