JFinal Handler 处理流程

第一、doFilter - 沿着 Handler 链,每个 handler 调用 handle 方法进行处理,然后交给下一个 handler。

容器初始化时访问 web.xml 配置的 JFinalFilter.doFilter。

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

    // 获取 request、response,设置编码
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;
    request.setCharacterEncoding(encoding);

    // 初始化的时候可以看出 contextPathLength 为 0 或者为 项目名称的长度
    // 比如 target = webapp/xx/yy/zz,则截取后的 target = /xx/yy/zz
    String target = request.getRequestURI();
    if (contextPathLength != 0)
        target = target.substring(contextPathLength);

    // 在调用了 ActionHandler的 handle 方法之后,isHandled[0] 才会被置为 true。
    boolean[] isHandled = {false};
    try {
        // 重头戏,handler 链首到链尾 ActionHandler 依次进行处理
        handler.handle(target, request, response, isHandled);
    }
    catch (Exception e) {
        if (log.isErrorEnabled()) {
            String qs = request.getQueryString();
            log.error(qs == null ? target : target + "?" + qs, e);
        }
    }

    // 若最后 isHandled[0] = false,会执行下一个 Filter。
    if (isHandled[0] == false)
        chain.doFilter(request, response);
}

JFinal 初始化过程中可以 add JFinal 库中的Handler 或自定义的 Handler。

例如:ContextPathHandler,JFinal 自身扩展的 Handler。

访问项目时就会走过 handler 方法设置 contextPath。这样在前端就可以通过 ${CONTEXT_PATH} 得到项目根路径。

public class ContextPathHandler extends Handler {

    private String contextPathName;

    public ContextPathHandler() {
        contextPathName = "CONTEXT_PATH";
    }

    public ContextPathHandler(String contextPathName) {
        if (StrKit.isBlank(contextPathName))
            throw new IllegalArgumentException("contextPathName can not be blank.");
        this.contextPathName = contextPathName;
    }

    public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
        request.setAttribute(contextPathName, request.getContextPath());
        System.out.println("哈哈哈");
        next.handle(target, request, response, isHandled);
    }
}

FakeStaticHandler,也是 JFinal 自身扩展的 Handler。new FakeStaticHandler 时可定义后缀,访问路径 target 必须是以这个后缀结尾才可以进行下去。

public class FakeStaticHandler extends Handler {

    private String viewPostfix;

    public FakeStaticHandler() {
        viewPostfix = ".html";
    }

    public FakeStaticHandler(String viewPostfix) {
        if (StrKit.isBlank(viewPostfix))
            throw new IllegalArgumentException("viewPostfix can not be blank.");
        this.viewPostfix = viewPostfix;
    }

    public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
        if ("/".equals(target)) {
            next.handle(target, request, response, isHandled);
            return;
        }

        if (target.indexOf(‘.‘) == -1) {
            HandlerKit.renderError404(request, response, isHandled);
            return ;
        }

        int index = target.lastIndexOf(viewPostfix);
        if (index != -1)
            target = target.substring(0, index);
        next.handle(target, request, response, isHandled);
    }
}

第二、访问交给 Handler 链尾 ActionHandler,调用 handle 方法进行处理:根据访问路径 target 得到 Action,由 Action 得到 Controller。接着 invoke 调用 Controller 的方法处理(此方法可根据 Action 获得)。

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

    isHandled[0] = true;
    String[] urlPara = {null};

    // actionMapping 根据 target 得到对应的 action
    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 {
        // 由 action 得到对应的 Controller
        Controller controller = action.getControllerClass().newInstance();

        // Controller 初始化
        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 {
            // 调用 Controller 相应的处理方法
            new Invocation(action, controller).invoke();
        }

        // 获取对应的 Render,如果是一个 ActionRender,就再交给 handler 处理;如果 Render == null会按照默认Render处理;
        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 实现类讲数据写入页面
        render.setContext(request, response, action.getViewPath()).render();
    }
    catch (RenderException e) {
        if (log.isErrorEnabled()) {
            String qs = request.getQueryString();
            log.error(qs == null ? target : target + "?" + qs, e);
        }
    }
    catch (ActionException e) {
        int errorCode = e.getErrorCode();
        if (errorCode == 404 && log.isWarnEnabled()) {
            String qs = request.getQueryString();
            log.warn("404 Not Found: " + (qs == null ? target : target + "?" + qs));
        }
        else if (errorCode == 401 && log.isWarnEnabled()) {
            String qs = request.getQueryString();
            log.warn("401 Unauthorized: " + (qs == null ? target : target + "?" + qs));
        }
        else if (errorCode == 403 && log.isWarnEnabled()) {
            String qs = request.getQueryString();
            log.warn("403 Forbidden: " + (qs == null ? target : target + "?" + qs));
        }
        else if (log.isErrorEnabled()) {
            String qs = request.getQueryString();
            log.error(qs == null ? target : target + "?" + qs, e);
        }
        e.getErrorRender().setContext(request, response, action.getViewPath()).render();
    }
    catch (Throwable t) {
        if (log.isErrorEnabled()) {
            String qs = request.getQueryString();
            log.error(qs == null ? target : target + "?" + qs, t);
        }
        renderFactory.getErrorRender(500).setContext(request, response, action.getViewPath()).render();
    }
}

new Invocation(action, controller).invoke();

public void invoke() {
    if (index < inters.length) {
        inters[index++].intercept(this);    // 调用拦截器的 intercept 方法,前后加一些东西
    }
    else if (index++ == inters.length) {    // index++ ensure invoke action only one time
        try {
            // Invoke the action
            if (action != null) {
                returnValue = action.getMethod().invoke(target, args);    // 调用该方法并且得到返回值
            }
            // else 不会运行到
            else {
                // if (!Modifier.isAbstract(method.getModifiers()))
                    // returnValue = methodProxy.invokeSuper(target, args);
                if (useInjectTarget)
                    returnValue = methodProxy.invoke(target, args);
                else
                    returnValue = methodProxy.invokeSuper(target, args);
            }
        }
        catch (InvocationTargetException e) {
            Throwable t = e.getTargetException();
            throw t instanceof RuntimeException ? (RuntimeException)t : new RuntimeException(e);
        }
        catch (RuntimeException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }
}

第三、接着上面 ActionHandler.handle 继续,Controller 的方法处理过程中创建了 render 对象,调用此方法后获取此实例进行页面展现。

在 Controller 方法处理的最后,往往要调用 render 方法来实例化 render 变量。例如,我自定义的一个 IndexController,在最后一步调用 render("next.html")。

public class IndexController extends Controller {
    public void index() {
        // ...
        render("next.html");
    }
}

用到父类 Controller 的 render 方法,通过 renderFactory.getRender 得到 render 实例。

public abstract class Controller {    

    // ...
    // render below ---
    private static final RenderFactory renderFactory = RenderFactory.me();

    /**
     * Hold Render object when invoke renderXxx(...)
     */
    private Render render;

    public Render getRender() {
        return render;
    }

    /**
     * Render with any Render which extends Render
     */
    public void render(Render render) {
        this.render = render;
    }

    /**
     * Render with view use default type Render configured in JFinalConfig
     */
    public void render(String view) {
        render = renderFactory.getRender(view);
    }

    /**
     * Render with jsp view
     */
    public void renderJsp(String view) {
        render = renderFactory.getJspRender(view);
    }

    /**
     * Render with freemarker view
     */
    public void renderFreeMarker(String view) {
        render = renderFactory.getFreeMarkerRender(view);
    }

    /**
     * Render with velocity view
     */
    public void renderVelocity(String view) {
        render = renderFactory.getVelocityRender(view);
    }

    /**
     * Render with json
     * <p>
     * Example:<br>
     * renderJson("message", "Save successful");<br>
     * renderJson("users", users);<br>
     */
    public void renderJson(String key, Object value) {
        render = renderFactory.getJsonRender(key, value);
    }

    /**
     * Render with json
     */
    public void renderJson() {
        render = renderFactory.getJsonRender();
    }

    /**
     * Render with attributes set by setAttr(...) before.
     * <p>
     * Example: renderJson(new String[]{"blogList", "user"});
     */
    public void renderJson(String[] attrs) {
        render = renderFactory.getJsonRender(attrs);
    }

    /**
     * Render with json text.
     * <p>
     * Example: renderJson("{\"message\":\"Please input password!\"}");
     */
    public void renderJson(String jsonText) {
        render = renderFactory.getJsonRender(jsonText);
    }

    /**
     * Render json with object.
     * <p>
     * Example: renderJson(new User().set("name", "JFinal").set("age", 18));
     */
    public void renderJson(Object object) {
        render = object instanceof JsonRender ? (JsonRender)object : renderFactory.getJsonRender(object);
    }

    /**
     * Render with text. The contentType is: "text/plain".
     */
    public void renderText(String text) {
        render = renderFactory.getTextRender(text);
    }

    /**
     * Render with text and content type.
     * <p>
     * Example: renderText("&lt;user id=‘5888‘&gt;James&lt;/user&gt;", "application/xml");
     */
    public void renderText(String text, String contentType) {
        render = renderFactory.getTextRender(text, contentType);
    }

    /**
     * Render with text and ContentType.
     * <p>
     * Example: renderText("&lt;html&gt;Hello James&lt;/html&gt;", ContentType.HTML);
     */
    public void renderText(String text, ContentType contentType) {
        render = renderFactory.getTextRender(text, contentType);
    }

    /**
     * Forward to an action
     */
    public void forwardAction(String actionUrl) {
        render = new ActionRender(actionUrl);
    }

    /**
     * Render with file
     */
    public void renderFile(String fileName) {
        render = renderFactory.getFileRender(fileName);
    }

    /**
     * Render with file
     */
    public void renderFile(File file) {
        render = renderFactory.getFileRender(file);
    }

    /**
     * Redirect to url
     */
    public void redirect(String url) {
        render = renderFactory.getRedirectRender(url);
    }

    /**
     * Redirect to url
     */
    public void redirect(String url, boolean withQueryString) {
        render = renderFactory.getRedirectRender(url, withQueryString);
    }

    /**
     * Render with view and status use default type Render configured in JFinalConfig
     */
    public void render(String view, int status) {
        render = renderFactory.getRender(view);
        response.setStatus(status);
    }

    /**
     * Render with url and 301 status
     */
    public void redirect301(String url) {
        render = renderFactory.getRedirect301Render(url);
    }

    /**
     * Render with url and 301 status
     */
    public void redirect301(String url, boolean withQueryString) {
        render = renderFactory.getRedirect301Render(url, withQueryString);
    }

    /**
     * Render with view and errorCode status
     */
    public void renderError(int errorCode, String view) {
        throw new ActionException(errorCode, renderFactory.getErrorRender(errorCode, view));
    }

    /**
     * Render with render and errorCode status
     */
    public void renderError(int errorCode, Render render) {
        throw new ActionException(errorCode, render);
    }

    /**
     * Render with view and errorCode status configured in JFinalConfig
     */
    public void renderError(int errorCode) {
        throw new ActionException(errorCode, renderFactory.getErrorRender(errorCode));
    }

    /**
     * Render nothing, no response to browser
     */
    public void renderNull() {
        render = renderFactory.getNullRender();
    }

    /**
     * Render with javascript text. The contentType is: "text/javascript".
     */
    public void renderJavascript(String javascriptText) {
        render = renderFactory.getJavascriptRender(javascriptText);
    }

    /**
     * Render with html text. The contentType is: "text/html".
     */
    public void renderHtml(String htmlText) {
        render = renderFactory.getHtmlRender(htmlText);
    }

    /**
     * Render with xml view using freemarker.
     */
    public void renderXml(String view) {
        render = renderFactory.getXmlRender(view);
    }

    public void renderCaptcha() {
        render = renderFactory.getCaptchaRender();
    }
}

根据 Controller 可以看出还能使用 renderJosn、renderText 等多种方法,这里一笔带过。

就先写到这里吧~~

时间: 2024-10-14 05:17:13

JFinal Handler 处理流程的相关文章

jfinal ——Handler 之 责任链模式(chain of)

Chain of ressponsibility 模式--推卸责任 1.什么是推卸责任? 将多个对象组成一条职责链,然后按照它们在职责链上的顺序来逐个找出到底应该谁来负责. 1.Chain of ressponsibility 模式的重要性 弱化"请求方"和"处理方"之间的关联关系,让双方各自都成为可独立复用的组件. 程序可对付其他需求,如根据情况不同,负责处理的对象也会发生变化的这种需求. 2.示例程序 示例程序类 Trouble 表示发生的问题的类.它带有问题的

Handler 机制(一)—— Handler的实现流程

由于Android采用的是单线程模式,开发者无法在子线程中更新 UI,所以系统给我提供了 Handler 这个类来实现 UI 更新问题.本贴主要说明 Handler 的工作流程. 1. Handler 的作用 在Android为了保障线程安全,规定只能由主线程来更新UI信息.而在实际开发中,会经常遇到多个子线程都去操作UI信息的情况,那么就会导致UI线程不安全.这时,我们就需要借助 Handler 作为媒介,让 Handler 通知主线程按顺序一个个去更新UI,避免UI线程不安全. 那么,子线程

android之handler机制深入解析

一.android中需要另开线程处理耗时.网络的任务,但是有必须要在UI线程中修改组件.这样做是为了: ①只能在UI线程中修改组件,避免了多线程造成组件显示混乱 ②不使用加锁策略是为了提高性能,因为android中经常使用多线程. handler就是为了解决在多个线程之间的通信问题. 二.基本使用: 1 package com.dqxst.first.multithread; 2 3 import android.app.Activity; 4 import android.os.Bundle;

Handler有何作用?如何使用?

一  Handler作用和概念 包含线程队列和消息队列,实现异步的消息处理机制,跟web开发的ajax有异曲同工之妙. 1.运行在某个线程上,共享线程的消息队列: 2.接收消息.调度消息,派发消息和处理消息: 3.实现消息的异步处理: Handler能够让你发送和处理消息,以及Runnable对象:每个Handler对象对应一个Thread和Thread的消息队列.当你创建一个Handler时,它就和Thread的消息队列绑定在一起,然后就可以传递消息和runnable对象到消息队列中,执行消息

关于handler和异步任务

handler使用流程概要 首先在主线程新建一个handler实例,重写onhandlemessage(Message msg) 方法,对传过来的message进行处理 然后在子线程中完成操作,操作完成后新建一个message对象,调用handler实例的sendmessage方法,将message传给消息队列,再由Looper分发给handler 异步任务使用流程 新建一个类,继承 AsyncTask<Void,Integer,Integer>,三个泛型分别是传入参数类型,进度类型,结果类型

Android中消息系统模型和Handler Looper

作为Android中大量使用的Handler,结合Thread使其具有众多的使用形式和方法, 让我一时感觉这个东西有些玄乎,不明所以然,这到底是一个什么样的存在呢?通过网上 资料和源码的学习,这个Handler也差不多弄清楚了,现在总结下这个学习结果. 一 Handler作用和概念 通过官方文档了解到Handler的大致概念是: Handler能够让你发送和处理消息,以及Runnable对象:每个Handler对象对应一个Thread和 Thread的消息队列.当你创建一个Handler时,它就

jfinal集成cas单点认证实践

本示例jfinal集成cas单点认证,采用获取到登录用户session信息后,在本地站点备份一份session信息,主要做以下几个步骤: 1.站点引入响应jar包: 2.在web.xml中配置对应过滤器: 3.增加拦截handler,并在jfinal的config中配置. 此次示例在handler中获取登录用户session,并设置本站点session. 详细介绍如下: 1.引入jar包 需要引入两个jar包:cas-client-core-3.2.2.jar和commons-logging-1

Handler详解系列(一)——Handler异步消息机制详解(附图)

MainActivity如下: package cc.cn; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import android.app.Activity; /** * Demo描述: * Android异步消息机制分析(附图) * * ===================

Handler机制源码分析

Handler机制 概念: handler机制是一种异步通信机制,通常用于子线程中数据更新后,通知主线程UI更新. handler运行框架图 从上面handler的运行框架图来看,为了完成handler整个流程,你必须按事先创建好四个东西: handler.Message.MessageQueue和Looper,也许Looper从上图来看并不是必须的,因为遍历MessageQueue只是调用了一个静态方法而已,并没有实例化一个Looper对象,既然这么想,那我们看看Looper.Loop()源码