本次回归码坛为小伙伴们带来的是重新定义过的 Template Engine 将极速开发继续贯彻到 View 层。
Java 模板引擎界已被 Freemarker、Velocity 统治多年,但其在这些年的发展可谓乏善可陈,究其根本原因是因为这个领域严重依赖于第三方工具去做词法、语法分析以及 AST 的构建,并未真正在语言层面进行过深入的追问。
例如 Freemarker、Velocity 是基于 javacc 做的二次开发,其他 java 模板引擎多数也是基于 ANTLR、jflex、javacc 生成后的代码做的二次开发全部自己动手写代码而不是二次开发的 java 模板引擎极为少见,国内较为知名的仅见 HTTL。
由于使用 ANTLR 等工具去做模板引擎可以让 80% 以上的工作由工具代为完成,所以对模板引擎作者极具诱惑力,但诱惑的背后其实是巨大的陷阱。
首先,这类工具无一例外都要求使用一套特定规则先表示出词法和语法,然后再把它们生成为 Parser 源代码而词法、语法描述用的规则和生成的 Parser 并没有很强的语义连接关系,如果生成的 Parser 有 bug,非常难以回溯排错。
其次,生成的 Parser 源码,毫无可读性而言,进而在 Parser 层面根本无法调试,这种构建模板引擎的方式,相当于将自己的代码建立在一个飘摇不安的基础之上。
再次,通过这种方式做出来的模板引擎如果要在更底层改进,则需要先去改语法、词法描述规则文件,然后再生成一次 Parser 源码,然后才可以开始真正写代码,这个重复、麻烦且容易出错的过程也严重阻碍了模板引擎的改进。
最后,这种构建方式通常在运行时还需要一个类似于 runtime.jar 的依赖,这个依赖多多少少也会增加不确定的风险。
模板引擎在本质上是一门程序语言,要开发一门可用于生产环境的模板语言起码要对开发程序语言的相关技术、算法很熟悉,例如常用词法分析算法、语法分析算法、AST、解释器、编译原理等等领域知识。
但非常不幸的是,即便对已有算法很熟悉,仍然只能做出极为平庸的模板引擎,因为模板引擎所处理的对象与程序语言有着十分不同的特征:模板的内容通常是极其自由的文本中嵌入着少量程序语言元素,而类似于 NFA 这样的算法都是面向类似于 java、c、python 这类高度结构化的源代码内容。
JFinal Template Engine 直面这个问题,采用独创的算法彻底代替传统算法,极大降低了复杂度,减少了代码量,同时可读性得到极大提升。JFinal Template Engine 经过公司内部开发者近三个月的试用时间,直到每位开发者都沉醉其中后才放出 jfinal 3.0 版本。
1:独创 DKFF(Dynamic Key Feature Forward) 词法分析算法
传统的 NFA 词法分析算法基于正则表达式与状态图,即便是做一个简单的模板引擎也需要成百上千个状态,NFA 算法对于人类来说几乎不可读,需要先将二维数组表示的状态图存储结构转换成画在纸上的人类可读的状态图,并且还要花费相当长的时间去解读其内涵。而 JFinal Template Engine 只需 7 个状态 403 行代码搞定词法分析,可读高,有利于系统进化。
2:独创 DLRD(Double layer Recursive Descent) 语法分析算法
针对模板文件大量纯文本掺杂少量代码特征,独创 DLRD 语法分析算法,将指令与表达式的语法分析划分在不同的层次之中相比传统递归下降语法分析算法,复杂度降到极致,代码量少到极致仅 215 行代码搞定 statement 语法分析。
DLRD 算法还极为轻松地解决了传统算法面对的左递归、二义性、回溯以及运算符优先级等麻烦问题,无需文法变换即可消除左递归,无需回溯即可解决二义性问题传统递下降法算法需要采用变换文法、回溯、提取公因子方式解决上述问题传统解决方案不仅麻烦而且让文法变换后的程序可读性降低,不利于进化。
3:独创语言级指令扩展
对于传统模板引擎来说指令系统是固定的,相当于程序语言的关键字,无法为语言动态添加新的关键字,JFinal template engine 得益于独创的 DKFF、DLRD 算法,极为轻松的在程序语言层面实现了指令扩展功能,从而整个模板引擎只需提供了极少的常用指令,其她所有指令均由扩展而成,例如 com.jfinal.template.ext.directive 以及com.jfinal.plugin.activerecord.sql 包之下的所有指令全是扩展而来。
基于以上主要创新, JFinal 在极简的路上继续挺进,一个 MVC + ORM 各层全面实现极速开发的框架由此诞生:
1:极简设计
JFinal Template Engine 消灭了大量传统模板引擎中的无聊概念,例如 macro、layout、pageContent、nested、tag 插值等等概念,但凡一门可用于生产环境的语言,只需利用表达式、语句、函数、分支、循环等基本要素,就可以像呼吸空气一样自由地实现上述这些功能,引入新的概念纯属制造麻烦。
以 macro 为例,该概念产生于很古老的程序语言,已证明是历史性糟粕在后起的程序语言中被抛弃,可却在 freemarker、velocity 等模板引擎中死而复生,以致于后来者也争相跟风模仿引入了 macro 概念。
2:独创空合安全取值调用操作符
JFinal Template Engine 引入了 swift 与 C#语言中的空合操作符,并在其基础之上进行了极为自然的扩展,该表达式符号为两个紧靠的问号: ??
value ?? rightExpr // 当value为null时,表达式取值rightExpr
object.value ?? // 当 object 为 null 时变为安全取值,不报空指针异常,表达式的值为null
object.method() ?? // 当 object 为 null 时变为安全调用,不报空指针异常,表达式的值为null
object.value ?? rightExpr // 空合并与安全取值的组合用法,当object为null时表达式取值rightExpr
object.method() ?? rightExpr // 空合并安全调用的组合用法,当object为null时表达式取值为rightExpr
swift 与 C# 语言仅支持第一种用法并称之为空合操作符,而 JFinal Template Engine 在空合的基础上再支持安全取值、安全调用两个扩展,在模板引擎的应用场景之下的开发体验极为顺滑。
3:消灭插值指令
传统模板引擎通常会引入所谓的插值指令,而其它指令则使用另一套规则,而 jfinal template engine 将插值指令仍然当成是指令,并不引进新的概念,不仅在实现上更加简洁只需 21 行代码,而且降低了学习成本
#(value) #(object.value) #(map.key)
4:消灭 macro 指令
如果真心将 template engine 当成一门语言去设计,原本用 macro 实现的功能只需要使用所有语言中都有的 function/method 概念,即可更为简单实现,如下是利用 fcuntion 实现 layout 功能的示例先在 layout.html 中定义 layout() 函数,并在 body调用 main() 函数
#define layout()
<html>
<body>
#@main()
<body>
</html>
#end
然后在最终的页面,直接调用 layout() 并定义 main() 函数
#@layout()
#define main()
...
#end
注意:以上的 layout.html、layout、main 这些名称可以随便取名,不是具体的概念,仅为一个名字
JFinal Template Engine 代码仅有 5556 行,仅为 freemarker 的十分之一,完全手写算法,无第三方依赖,对于有任何一门程序语言的开发者学习成本几乎为 0。为控制篇幅,在此不在赘述。jfinal 3.0 版不仅仅是重新定义了 Template Engine,还对其她方面做了非常有价值的改进
1:Routes 级拦截器
jfinal 3.0 可以在 Routes 中添加拦截器,Routes 极拦截器,将拦截所有在此 Routes 中映射的 Controller:
public class AdminRoutes extends Routes {
public void config() {
addInterceptor(new AdminInterceptor());
add("/admin", IndexAdminController.classs);
add("/admin/project", ProjectAdminController.classs);
}
}
上例中的 AdminInterceptor 将拦截 IndexAdminController、ProjectAdminController 中的所有 action,这非常有利于多个控制器配置了相同拦截器的应用场景,减少了代码冗余。
2:active record 模块 sql 管理模块
JFinal 3.0利用自带的Template Engine极为简洁的实现了Sql管理功能,可以将sql保存在外部配置文件之中,并且以极少概念极少 API 实现 mybatis 的核心功能
#sql("findPrettyGirl")
select * from girl where age > ? and age < ? and weight < 50
#end
java 代码中的使用方式:
find(getSql("findPrettyGirl"), 16, 20);
同时还提供了 namespace 支持与 para 动态绑定功能,详见 jfinal 手册
3:Cron4jPlugin 支持任务调度
Cron4jPlugin 对 cron4j 进行了极简封装,简单用法如下:
Cron4jPlugin cp = new Cron4jPlugin();
cp.addTask("* * * * *", new MyTask());
me.add(cp);
外部配置文件用法详见 jfinal 手册
4:renderQrCode 支持二维码生成
添加 Controller.renderQrCode(…) 极速生成二维码
jfinal 3.0 一如既往地坚持极简设计风格,在极速开发的路上一路挺进,力争为开发者带来更高的开发效率,更好的开发体验,更低的学习成本,进一步节约时间去陪恋人、家人和朋友!篇幅所限,更多功能增强与改进详见 changelog: http://www.jfinal.com/share/190
ONE MORE THING:JFinal 俱乐部频道今起正式开放,将为小伙伴们提供更高附加值的产品与服务进一步提升开发效率,节省开发时间。老司机要发车了,GO GO GO:http://www.jfinal.com/club