jfinal源码研究之核心组件Render

作为jfinal中的五大组件之一,Render的主要职责是将请求处理结果以相应的方式返回给客户端。即Render抽象隔离了纷杂的前端展示逻辑,对外提供了统一的调用接口。

1. 概述

对于该核心组件,jfinal依然是给予了足够的地位, 提供了顶级package —— com.jfinal.render,与Render相关的实现基本都是位于本package中。

2. 定义

对于Render的定义,jfinal依然采取的是意料之外,情理之中的设计——将其设计为了抽象类, 而非一般我们认为的接口。

这里只保留关键性的字段和方法

public abstract class Render {
    // 关键性字段就这三个,排除掉Servlet规范,剩下的就是作为模板路径的view了。
    // 得益于Servlet的优秀设计, Render的执行上下文设置相当简单; 而且契约方法的声明里没有返回值, 对外部的感知更少
    protected String view;
    protected HttpServletRequest request;
    protected HttpServletResponse response;

    // jfinal选择了外界主动调用的方式来设置render操作时的Context。
    // 而且注意jfinal里这里的API设计依然采用其最常用的返回自身的设计方式, 这样外界调用时就可以进行非常流畅的链式操作,值得借鉴。
    public Render setContext(HttpServletRequest request, HttpServletResponse response, String viewPath) {
        this.request = request;
        this.response = response;
        if (view != null && view.length() > 0 && view.charAt(0) != '/') {
            view = viewPath + view;
        }
        return this;
    }

    /**
     * Render to client
     */
    // 本抽象类唯一的抽象方法
    // 也是核心方法, 负责将相应的内容渲染到客户端
    public abstract void render();

}

以上虽然省略了一些代码,但依然可以看出Render的设计还是相当简单和清晰的,唯一的render方法将全权交由子类来完成自身的自定义逻辑,Render不进行任何的假设性逻辑。

3. 继承链

对于Render,jfinal提供的默认实现已经相当丰富,基本可以满足日常的开发需求。但开发者依然可以按照自己的需求来进行自定义扩展(作为 C 层基类的Controller就提供了一个render(Render render)方法来将渲染逻辑全权交给框架使用者);亦或者在其他项目中借鉴其思想,来简化自身的设计。其实本人会阅读jfinal源码,主要原因是希望在Spring之外给自己另外一种选择。
Render继承链

以上图中的继承链,类名基本上已经能够清晰地说明其所关注的渲染逻辑。所以接下来我们将不再作列表式陈诉,而只是挑选出一些有代表性的进行探讨:

  1. com.jfinal.ext.render.CaptchaRender 。 在3.4版本中已经被jfinal 进行了@Deprecated标注 ; jfinal官方目前更加推荐直接使用 Controller.renderCaptcha()
  2. jfinal目前支持的模板引擎从图中就可见一斑——FreeMarker,Jsp,Velocity,自身。

4. 生命周期

接下来让我们看看在jfinal处理一次请求的生命周期中Render是如何完成自身职责的。

对于jfinal框架的使用者而言,在其对Action的实现中,不出意外的话基本都是以renderXxx结尾;jfinal官方也是如此推荐——即使最终选择不进行任何渲染操作,也请主动调用一次renderNull方法(很明显的Null模式,内部实现为空)。

而对于自定义Action的调用,jfinal是放在了ActionHandler中的handle方法中。

// ActionHandler.handle
// 这里我们只贴出本次相关的代码
public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {

    // 这里将回调用户的自定义Action逻辑实现
    new Invocation(action, controller).invoke();
    // 取出用户自主选择的render实例(用户通过主动调用renderXxx来实现自主选择render实例)
    Render render = controller.getRender();
    if (render == null) {
        render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName());
    }
    // 设置render的上下文, 并进行render操作
    // 执行权限被全权交给了Render.render。jfinal只会等待 Render.render 的完全执行完毕,由render将控制权主动归还。
    render.setContext(request, response, action.getViewPath()).render();

    // 以下,除非发生异常, 否则本次请求处理逻辑基本算是到此结束了.
}

5. 补充

  1. 由Controller类提供的方法controller.getRender()可知,Render是属于Controller一个级别的; 而Controller按照之前的讨论,对于每次请求都是重新生成一个相应的全新实例;所以是不存在所谓的线程资源争抢的问题。
  2. 级联关系如下:RenderManager >> RenderFactory (抽象工厂模式) >> Render。
  3. 对于RenderFactory ,jfinal在Constants配置时提供了相应的自定义配置方法 —— setRenderFactory , 允许框架使用者提供完全自定义的Render构造逻辑。
  4. 说句题外话,本人比较喜欢jfinal在实现单例模式时的命名,例如这里的RenderManager。
// 单例模式(饿汉模式)
private static final RenderManager me = new RenderManager();
private RenderManager() {}

public static RenderManager me() {
    return me;
}
  1. RenderFactory

猜你喜欢

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