實現本身的控制層do-c (仿Struts2和SpringMVC)(八)

上篇博客中,咱們創建了一個TransServlet類,它被用來處理系統中全部的請求,也即,TransServlet是程序的入口。同時咱們也在TransServlet中訪問了DemoC類中的helloString()方法,可是訪問方式是直接硬編碼的,本篇咱們將會把這些硬編碼的代碼修改爲動態的,請不要忘記在第六篇博客中咱們創建了一個註解C,還不要忘記DemoC類上有註解C,這們就要從這些註解入手了。同時上篇遺留的一個問題就是在地址欄輸入java

http://127.0.0.1:2016/webby/demo/helloString.ts?hello=helloc

後,程序能判斷出目標類名及具體方法就行了,這也是本篇博客的目的。git

其實在TransServlet類中咱們將要按順序作以下功能:web

  1. 在service()方法中攔截全部請求
  2. 根據請求路徑判斷出請求的去處,即請求要訪問的是哪一個控制層類,即被註解C標記的類,如DemoC。
  3. 再根據請求路徑判斷出請求到要達哪一個具體的方法,如DemoC的helloString()方法。
  4. 返回相應數據,跳轉到頁面或跳轉到其它控制層。

還記得service()方法的內容吧,爲了說明,再貼一下。數組

@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {
    // System.out.print(System.currentTimeMillis());
    
    DemoC demoC = new DemoC();
    demoC.helloString("這是DemoC");        
}

1. 解析請求字符串

咱們要作的第一步就是獲取請求字符串。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

2. 獲取目標控制層類

從請求路徑中咱們已經獲取到了demo/helloString,其中前者是控制層類,後者是指定的方法名。此時咱們須要一個工具類來保存控制層名稱與類的對應關係,這個類要在項目啓動的時候咱們要收集全部被註解C標記的類,將這些類存放以便之後使用。工具

獲取目標控制層類就顯得方便了,在工具類中提供對應的獲取方法就行。測試

3. 反射進行方法的調用

上一步咱們獲得了方法名。咱們要獲得目標控制層類的指定方法。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

相關文章
相關標籤/搜索