上篇博客中,咱們創建了一個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