实现本身的控制层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

相关文章
相关标签/搜索