作为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之外给自己另外一种选择。
以上图中的继承链,类名基本上已经能够清晰地说明其所关注的渲染逻辑。所以接下来我们将不再作列表式陈诉,而只是挑选出一些有代表性的进行探讨:
com.jfinal.ext.render.CaptchaRender
。 在3.4版本中已经被jfinal 进行了@Deprecated标注 ; jfinal官方目前更加推荐直接使用Controller.renderCaptcha()
。- 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. 补充
- 由Controller类提供的方法
controller.getRender()
可知,Render是属于Controller一个级别的; 而Controller按照之前的讨论,对于每次请求都是重新生成一个相应的全新实例;所以是不存在所谓的线程资源争抢的问题。 - 级联关系如下:RenderManager >> RenderFactory (抽象工厂模式) >> Render。
- 对于RenderFactory ,jfinal在Constants配置时提供了相应的自定义配置方法 ——
setRenderFactory
, 允许框架使用者提供完全自定义的Render构造逻辑。 - 说句题外话,本人比较喜欢jfinal在实现单例模式时的命名,例如这里的RenderManager。
// 单例模式(饿汉模式)
private static final RenderManager me = new RenderManager();
private RenderManager() {}
public static RenderManager me() {
return me;
}