源码有毒:Jfinal源码解析(五)

400 查看

前面把JFianl框架的初始化过程大概理了一遍,接下来,看一下JFinal接受到一个Http请求之后是如何相应的。
回到web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
    <filter>
        <filter-name>jfinal</filter-name>
        <filter-class>com.jfinal.core.JFinalFilter</filter-class>
        <init-param>
            <param-name>configClass</param-name>
            <param-value>com.css.mission.MainConfig</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>jfinal</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

这里配置的jfina Filter是“/*”,也就是说这个Filter或拦截所有的请求。当请求过来之后,都会进入JFinalFilter的doFilter方法

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        request.setCharacterEncoding(encoding);

        String target = request.getRequestURI();
        if (contextPathLength != 0)
            target = target.substring(contextPathLength);

        boolean[] isHandled = {false};
        try {
            handler.handle(target, request, response, isHandled);
        }
        catch (Exception e) {
            if (log.isErrorEnabled()) {
                String qs = request.getQueryString();
                log.error(qs == null ? target : target + "?" + qs, e);
            }
        }

        if (isHandled[0] == false)
            chain.doFilter(request, response);
    }

前几行代码没什么说的,获取request,response,设置编码,截取除主机名之后的action请求。
boolean[] isHandled = {false};这行代码就比较有意思了,这里作者用了一个boolean类型的数组来做标记位,标记是否需要doFilter这个请求,有人就会有疑问,为什么不直接用一个boolean类型的变量来做标记,而要用一个数组。《Java虚拟机规范》有一段对boolean类型的描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位“,《Java虚拟机规范》给出了boolean类型4个字节,和boolean数组1个字节的定义。这里可以看作是作者对内存的一个小的优化,至于Java虚拟机为什么要用4个字节代表boolean类型的值,而不用byte或者short,这里就不再深究,有兴趣的可以自己去查下相关资料。继续往下看

try {
            handler.handle(target, request, response, isHandled);
        }
        catch (Exception e) {
            if (log.isErrorEnabled()) {
                String qs = request.getQueryString();
                log.error(qs == null ? target : target + "?" + qs, e);
            }
        }

这里调用了handler.handle(target, request, response, isHandled);我们知道handler在初始化的时候被赋值的,对应的是ActionHandler这个类的实例。接着看ActionHandler.handle(target, request, response, isHandled);

public final void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
        if (target.indexOf('.') != -1) {
            return ;
        }

        isHandled[0] = true;
        String[] urlPara = {null};
        Action action = actionMapping.getAction(target, urlPara);

        if (action == null) {
            if (log.isWarnEnabled()) {
                String qs = request.getQueryString();
                log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs));
            }
            renderFactory.getErrorRender(404).setContext(request, response).render();
            return ;
        }

        try {
            Controller controller = action.getControllerClass().newInstance();
            controller.init(request, response, urlPara[0]);

            if (devMode) {
                if (ActionReporter.isReportAfterInvocation(request)) {
                    new Invocation(action, controller).invoke();
                    ActionReporter.report(controller, action);
                } else {
                    ActionReporter.report(controller, action);
                    new Invocation(action, controller).invoke();
                }
            }
            else {
                new Invocation(action, controller).invoke();
            }

            Render render = controller.getRender();
            if (render instanceof ActionRender) {
                String actionUrl = ((ActionRender)render).getActionUrl();
                if (target.equals(actionUrl))
                    throw new RuntimeException("The forward action url is the same as before.");
                else
                    handle(actionUrl, request, response, isHandled);
                return ;
            }

            if (render == null)
                render = renderFactory.getDefaultRender(action.getViewPath() + action.getMethodName());
            render.setContext(request, response, action.getViewPath()).render();
        }
    catch (RenderException e) {
        ...异常处理
    }
}

首先方法进来

if (target.indexOf('.') != -1) {
            return ;
        }

这里的作用是过滤掉资源请求,比如http://localhost:8080/test.html、http://localhost:8080/imge/2.png

        isHandled[0] = true;
        String[] urlPara = {null};
        Action action = actionMapping.getAction(target, urlPara);

这里先把标记的标记改为true,说明当前要消耗掉这个请求。actionMapping.getAction(target, urlPara);根据用户的请求地址从actionMapping中获取相应的Action

Action getAction(String url, String[] urlPara) {
        Action action = mapping.get(url);
        if (action != null) {
            return action;
        }

        // --------
        int i = url.lastIndexOf(SLASH);
        if (i != -1) {
            action = mapping.get(url.substring(0, i));
            urlPara[0] = url.substring(i + 1);
        }

        return action;
    }

先是更具用户的请求直接从map集合中获取Action,如果没有获取到,则截取到最后一个”/“之前的请求,作为请求url从,重新从map集合中获取Action,使得/user/login/sdfsfsdfsdfs之类的错误请求能定位到/user/login/,只是现在还不理解作者这里处理的用意是什么。个人觉得这样的设计会反而会增大项目的风险,也可能是我对框架的理解不够透彻。如果你对这里的代码有更好的理解欢迎在下方留言或给我私信