jfinal源码研究之核心组件Handler

之前的两篇文章里我们已经从全局的角度泛谈了jfinal的整体逻辑。接下来我们将以一个比较深入的视角来分别探讨下jfinal中的各类核心组件。

1. 概述

JFinal 采用微内核全方位扩展架构,全方位是指其扩展方式在空间上的表现形式。JFinal由Handler、Interceptor、Controller、Render、Plugin五大部分组成。

而诚如标题所言,本文的关注中心将是jfinal中的核心组件Handler。

2. 定义

现在先让我们来看看Handler的定义。首先,第一次看到这个类的时候我可以说是有点小意外,因为在我的预想里面,这个Handler应该是作为一个接口存在,但没想到jfinal直接将其定义为了一个抽象类。其实这也是有着相当的合理性的:现在的很多大型框架里,为了增加框架的适应性和可扩展性,往往一个功能的继承链都是从一个接口开始,然后依次是AbstractXxx,XxxImpl,XxxImplWrapper,XxxAdapter等等;这么一套下来,有的时候真的让人感觉蛋疼无比。诸如Spring里的不少接口,2002年就定义了,但直到如今也没见到第二个实现。那么这种设计的合理性就需要掂量一下了。

jfinal一直宣称的“功能强大,学习简单”在这里就能体现一二了,其直接简化了继承链体系,直接去掉了接口这一层的继承链,并且下方的继承链截图中我们可以看到,Handler的继承链只有两层,这就让理解起来轻松很多;读者可以对比下Spring的BeanFactory的继承链,或者对.NET有所涉猎的可以去看看WPF里控件的一字常长蛇阵般的继承体系。继承链可以增加框架的灵活性,但同时它也会大大增加使用者对于框架的理解难度。世间的万物有利必有弊!

/**
 * Handler.
 * <p>
 * You can config Handler in JFinalConfig.configHandler() method,
 * Handler can do anything under the jfinal action.
 */
public abstract class Handler {

    /**
     * The next handler
     */
    protected Handler next;

    /**
     * Use next instead of nextHandler
     * 该字段和上面的next完全等价,之所以保留完全是为了向后兼容。
     */
    @Deprecated
    protected Handler nextHandler;

    /**
     * Handle target
     * @param target url target of this web http request
     * @param request HttpServletRequest of this http request
     * @param response HttpServletRequest of this http request
     * @param isHandled JFinalFilter will invoke doFilter() method if isHandled[0] == false,
     *          it is usually to tell Filter should handle the static resource.
     */
    public abstract void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled);
}

Handler从其定义上就清晰得表明其是一个链表结构。这一点上也非常契合过往的MVC框架里对web请求的处理方式。

Handler.handle作为该接口唯一的方法定义, 其子类可以使用如下两种方式来控制整体的请求处理逻辑扭转:
1. 在handle的实现中通过主动调用next.handle来将请求处理逻辑交给下一个Handler
2. 通过给方法的第四个参数赋值 isHandled[0] = true 来告知jfinal的核心Filter——JFinalFilter不要将请求处理逻辑交给之后的Filter了,本次请求的处理工作已经完毕了。
3. 所以next.handle控制的是jfinal**内部**的逻辑跳转;而 isHandled[0] = true则是负责控制Servlet层面的逻辑跳转; 这里注意区分下影响范围, 虽然最终的表示形式可能一致。

3. 继承链

只盯着一个抽象类,我们能得到的信息有限,接下来就让我们看看Handler在jfinal中的继承链体系。

Handler继承链

以上是jfinal3.4版本为止,提供所有内置Handler扩展,现在就让我们一个个得对其作一个简要的分析。

3.1 ContextPathHandler
  1. 这个类为view层的上下文提供了一个context path的键值对。
    2, 通过配置该Handler, 你可以在View层的模板中通过${CONTEXT_PATH}来获取到本项目的request.getContextPath() 值。这样系统的灵活性就得到了加强。
3.2 DruidStatViewHandler
  1. 该Handler依然是jfinal提供的内置扩展,用来调度druid提供的监控功能。针对满足匹配条件的请求,该Handler会承接所有的请求处理逻辑。不会将逻辑向后传递给其他Handler或Filter。
  2. 我们可以从中得到启发,再有类似的功能需求可以直接借鉴其实现方式来做。
3.3 FakeStaticHandler
  1. 对于习惯了SpringMVC,struts2的请求路径的开发人员,可能一开始对jfinal中请求路径里没有后缀感到些许的不适应。此时我们就可以通过配置该Handler来模拟类似的效果。
  2. 默认的后缀是.html; 当然可以通过提供的构造函数参数来修改这一默认行为。
3.4 ServerNameRedirect301Handler
  1. 负责处理301重定向的Handler。该Handler会监控每一个请求, 对于满足条件的请求自动返回一个301的状态码,以及相应的最终URL地址。
  2. 注意到目前位置,其还没有提供通配符功能,所以如果有多个类似的需求,你需要配置多个该Handler。
3.5 UrlSkipHandler

该Handler的主要功能是依据配置的正则匹配符,筛选出不被JFinalFilter处理的请求,以交给JFinalFilter之后的其他Filter去处理。即该请求不属于JFinalFilter的处理范围。

3.6 ActionHandler

该Handler作为jfinal中当之无愧的核心Handler,已经在之前的文章jfinal源码研究之处理请求中进行了涉猎,这里就不再赘述。

3.7 自定义Handler

另外让我们来看看jcbase中对Handler的扩展,借此见识下Handler的实际的应用场景,以此有一个比较深入的了解。

// ------------------------------------- ResourceHandler
/**
 * 资源地址初始化
 * 这个类的功能和jfinal提供的ContextPathHandler比较类似,不过因为自定义的原因,所以更加灵活,读者可以根据自身需求自行取舍。
 */
public class ResourceHandler extends Handler {

    @Override
    public void handle(String target, HttpServletRequest request,
            HttpServletResponse response, boolean[] isHandled) {
        // 统一设置basePath
        ServletContext context = request.getServletContext();
        if (context.getAttribute("res_url") == null) {
            context.setAttribute("static_url", PropKit.get("static_url"));
            String context_path=context.getContextPath().equals("/")?"":context.getContextPath();
            context.setAttribute("res_url", context_path+"/res/");
            context.setAttribute("context_path", context_path);
        }
        next.handle(target, request, response, isHandled);
    }

}

// ------------------------------------- SessionIdHandler
/**
 * 有时候session相关的id会是由url来承载,此时为了适配jfinal中的ActionHandler, 我们就需要截取掉这一部分
 */
public class SessionIdHandler extends Handler {

    @Override
    public void handle(String target, HttpServletRequest request,
            HttpServletResponse response, boolean[] isHandled) {
        boolean isFromURL = request.isRequestedSessionIdFromURL();
        if (isFromURL) {
            target = target.substring(0, target.indexOf(';'));
        }
        next.handle(target, request, response, isHandled);
    }

}

4. 总结

在大致探究了Handler的定义和继承链体系之后,接下来研究一些细节性内容。

  1. Handler通过JFinalConfig.configHandler(Handlers me)来配置自定义的Handler。最后这些Handler会和jfinal默认添加的ActionHandler一起组织成链式结构(借助Handler中定义的next字段)。用户添加进参数Handlers中的顺序即为该链式结构的顺序,也是用户请求依次经过Handler的顺序。并且该链式结构的最后一个Item一定是ActionHandler——即用户请求正常情况下最终将被ActionHandler处理。关于该链式结构的细节可以查看源码HandlerFactory.getHandler
  2. Handler实现类中,由调用者自主决定是否将处理流程向后传递,框架不负责这方面的逻辑调度,将该权限全部下放给Handler。 所以除非明确清楚逻辑到此结束,否则在Handler.handle的实现中必须主动发起调用next.handle,来确保逻辑继续。若不主动调用next.handle 则代表请求不交给下一个Handler, Handler请求链条针对该请求的处理到此为止。
  3. 另外在Handler.handle的实现中,我们可以通过设置第四个方法参数isHandled[0] = true;来控制是否将请求发给其他Filter去处理,这里的isHandled[0] = true;代表本次请求由本Filter全权处理了,不需要交给接下来的Filter。
  4. 在经过一系列用户配置的前置Handler处理后,请求被最终传递到了ActionHandler,jfinal将在这里回调用户自定义的Action和配置的相关Interceptor。更多的细节我们将在Interceptor一章中论述,这里就不再讨论了。
  5. 所以Handler可以接管所有web请求,并对应用拥有完全的控制权,可以很方便地实现更高层的功能性扩展。
  1. configHandler - Office Site

猜你喜欢

转载自blog.csdn.net/lqzkcx3/article/details/81044844