Beetl模板语言使用指南
什么是Beetl
Beetl是Bee Template language,Bee译为忙碌的人,意指忙碌中国的开发人员。目前版本0.6beta,大小约320K
Beetl是国人提供的一款开源免费得模板语言,作者有10余年Java开发经验,曾在国内外著名大公司工作过,根据自己实际使用模板语言的心得体会而编写的一款模板语言,它具有如下特性:
1 非常简单:它的语法是javascript一个子集,只有少量的大家熟悉的符号。任何了解java,或者javascript的人,都能快速学会。如果从未用过任何模板语言,用Beetl是非常很合适的
2 同时支持较为松散的MVC和严格的MVC,如果在模板语言里嵌入计算表达式,复杂条件表达式,以及函数调用有干涉业务逻辑嫌疑,你可以禁止使用这些语法。关于这一点,可以参考strictly enforces model-view separation
3 提供一系列其他模板语言没有提供的功能,如自定义占位符号,控制语句符号,虚拟属性,自定义函数,文本处理函数等,它们并不复杂,但有可能解决你在使用别的模板语言时候遇到的一些不便捷的问题
Beetl能为你做些什么
作为模板语言,你可以用于任何适合在MVC的地方。如代码生成,或者web界面,
因为Beetl是基于antlr实现语法解析的,因此如果你仅仅对antlr感兴趣,beetl仍然可以作为你的一个重要参考
关于Beetl性能:
目前实现了runtime版本,适合代码生成。暂时不适合作为web界面。它本生的是以易读的方式实现,并未经过优化。然而,即将推出预编译版本,可以保证有很好的性能
Beetl目前渲染一个7K文件,内含少量控制语句和占位符,所需要时间是1毫秒,这是在我一个四年前的老机器上跑得,作为代码生成,你完全无需担心性能。
|
beetl(runtime,0.52) |
Beetl(runtime,0.6) |
Freemarker(2.3.1.8) |
Beetl(compiled) |
Velocity |
7K(1000次) |
4000毫秒 |
950毫秒 |
900毫秒 |
估计约400毫秒 |
|
展望Beetl预编译实现出来后,性能将至少提高2-3倍以上,因此未来能超越Freemaker
关于功能:
http://freemarker.sourceforge.net/fmVsVel.html 是一篇freemaker与velocity功能比较的文章,很幸运Beetl能以简单易学,更易扩展的方式支持所有功能。
下表是以此文章为基础做的比较
功能点 |
Beetl |
Freemarker |
velocity |
Number and date support |
yes |
yes |
no |
Internationalization: |
Yes,但不支持中文变量名 |
yes |
no |
Loop handling: |
Yes,better |
yes |
no |
Array handling on the template language level |
yes |
yes |
no |
Macros |
Yes |
Yes |
no |
Name-spaces: |
No |
yes |
no |
Java-independent string, list, and map manipulations with built-in functions/operators: |
yes |
yes |
no |
Expose typos and other mistakes in template |
Yes,better |
yes |
no |
Advanced rendering control: |
yes |
yes |
no |
Literals: |
yes |
yes |
no |
Advanced white-space removal |
No ,不明白为啥有此需求 |
yes |
no |
Integration with other technologies: |
yes |
yes |
yes |
Powerful XML transformation capabilities: |
no |
yes |
no |
Advanced template metaprogramming: |
No,不明白为啥有此需求 |
yes |
no |
function |
No,觉得模板不需要 |
yes |
No |
自定义控制语句 |
yes |
no |
no |
自定义占位符号 |
yes |
no |
no |
严格MVC控制 |
yes |
no |
no |
虚拟属性 |
yes |
no |
no |
文本处理函数 |
yes |
no |
no |
自定以错误处理Hanlder |
yes |
no |
no |
基本用法
Hello Beetl
package org.bee.tl.samples; import java.io.IOException; import org.bee.tl.core.BeeTemplate; public class HelloBeetl {
public static void main(String[] args)throws IOException { Template template =new BeeTemplate("Hello,$name$");// 1 template.set("name","Beetl");//2 String result = template.getTextAsString();//3 System.out.println(result); } }
|
1用于BeeTemplate创建一个模板,此时使用的是一个字符串输入,输入也可以是java.io.File或者java.io.reader.对于beetl来说,如果输入是文件,那将会缓存中间的解析结果而大幅度提升性能
2定义变量,set方法允许字符串,对象作为参数,如果需要引用对象的属性,则用小数点,如$user.name$,如果属性是个List集合,可以用[索引],如$user.friends[0]$,如果属性是Map集合,
使用[key],key为任何对象,如$books[‘thinking in java’].author$
3调用template.getTextAsString() 或者template.getText(OutputStream os)都可以获得模板渲染的结果
控制语句和占位符号
Beetl默认情况下,采用#:作为控制语句开始,回车作为控制语句结束
#:for(user in userList){ hello,$user.name$ #:} |
默认情况下,占位符号使用$作为开始和结尾占位符号
$users[index]$ |
然而,Beetl支持自定义控制语句和占位符号,以适应不同类型模板文件
public static void main(String[] args) { String input = ”…..” BeeTemplate template =new BeeTemplate(input); template.setStatementStart("<%"); template.setStatementEnd("%>"); template.setPlaceholderStart("~"); template.setPlaceholderStart("~"); template.getTextAsString(); } |
此代码可以解析如下模板文件
<% var temp=”lijz”; %> Hello ~temp~ ! |
建议:控制语句和占位符号最好不要影响原有文件,可以使用<!--: -->作为XML模板文件控制语句,使用#:作为通常shell脚本,配置文件的控制语句
API接口
Beetl 主要的接口Template 和 类 GroupTemplate.
Template 实现类分为BeeTemplate 和 CompiledBeeTemplate,前者用于解释执行模板,适合代码生成或者开发阶段使用,后者是预编译成class,适合web框架或者生产模式使用。
org.bee.tl.core.Template 常用API |
|
public void set(String name, Object o) |
设置模板变量 |
public void getText(OutputStream os) |
渲染模板到os |
public void getText(Writer w) |
渲染模板到wirter |
public String getTextAsString() |
渲染模板,结果作为String返回 |
public void makeStrict(boolean isTrict) |
是否使用严格MVC |
org.bee.tl.core.Template 高级API (同org.bee.tl.core..GroupTemlate) |
|
public void setPlaceholderStart(String placeholderStart) |
设置占位符好开始标记,默认是$ |
public void setPlaceholderEnd(String placeholderEnd) |
设置占位符好结束标记,默认是$ |
public void setStatementStart(String statementStart) |
设置控制语句开始标记,默认是#: |
public void setStatementEnd(String statementEnd) ; |
设置控制语句结束标记,默认是null,也就是文件回车换行符号 |
public void registerFunction(String name,Function fn); |
为Beetl注册一个自定义函数,参考高级用法 |
public void registerFormat(String name,Format format); |
为Beetl创建一个格式化函数,参考高级用法 |
public void registerTextProcess(String name,TextPorcessFunction process); |
为Beetl创建一个文本处理函数,参考高级用法 |
public void registerVirtualAttributeEval(VirtualAttributeEval e); |
为某对象设置一个虚拟属性,参考高级用法 |
Beetl 不推荐直接调用高级API,在一个真正的系统里,首先通过GroupTemlate设置好模板所有属性,然后调用 GroupTemlate.getTetmplate来获取Template,如下代码
package org.bee.tl.samples; import org.bee.tl.core.GroupTemplate; public class GroupTemplateUtil { static GroupTemplate group = new GroupTemplate(); static {| group.setPlaceholderStart("$"); group.setPlaceholderEnd("$"); group.setStatementStart("#:"); group.setStatementEnd(null); group.makeStrict(true); } public static GroupTemplate getGroup (){ return group; }
|
org.bee.tl.core.GroupTemplate 常用API |
|
public GroupTemplate() |
构造一个GroupTemplate |
public GroupTemplate(File root) |
构造一个GroupTemplate,且制定模板文件跟目录,此目录下的模板文件都将编译成class,(前提是isProduct = true |
public Template getStringTemplate(String input) |
得到一个BeeTemplate |
public Template getReaderTemplate(Reader reader) |
得到一个BeeTemplate,输入是Reader |
public Template getFileTemplate(String child) |
如果isProudct=true,得到一个CompiledBeeTemplate模板,否则得到一个BeeTemplate |
public void setProduct(boolean isProduct) |
指示Beetl运行在开发模式还是生产模式 |
注意:要真正获得预编译支持以用于Web或者高性能项目, 只有通过GroupTemplate(File root) 构造的GroupTemplate ,且sProudct=true ,
然而,目前暂时不支持预编译版本
定义变量
Beetl允许定义变量,准确的说,允许定义临时变量,如下所示
#:var name=’lijz’,loopCount=100+other ,girlName; |
关键字var是必须得,这点不同于javascript
Beelt中得变量同javascript一样,有自己的作用域,如下模板输出为”lucy”
#:var name=’lijz’,i=1; #:if(i>=1){ #:var name=’lucy’; Hello,$name$ #:} |
变量命名规则同javascript或者java,但不允许以俩个下划线开头"__",这是因为以此开头的多为Beetl内部的一些临时变量
算术表达式
Beetl支持类似javascript的算术表达式和条件表达式,如+ - * / % 以及(),如下例子
#:var a=1,b=2,c=3; the result is $(a+b)*c-0.75$ |
逻辑表达式
Beetl支持类似Javascript,java的条件表达式 如>, <, == ,!=,>= , <= 以及 !,如下例子
#:var a=1,b=2,c=3;if((b-c)>=1){ Hello big! #:}else{ :( ,small! #:} |
循环语句
Beetl 支持for in 循环格式,以及break,continue,return (实际上可以出现在任何地方),如下例子
//java代码 tempalte.set("userList",userList); //模板 总共 $userList.~size$ #:for(user in userList){ $user.~index$ . Welcome $user.name$! #:} |
如果循环中,需要引用当前索引和总数,可以分别使用虚拟属性index,size
条件语句
Beetl只支持同javascript,java一样的if 语句,不支持switch. 如下例子
#:var isGirl = true; #:if(isGir){ 姑娘,你好 #:}else{ 小伙子,你好 #:} |
函数调用
Beetl内置了少量实用函数,可以在Beetl任何地方调用,一般情况是在占位符里调用。
如下例子是调用NVL函数,判断如果变量为不为null,则输出变量,如果为null,则输出默认值
//java代码 template.set("name",service.getName(id)); //模板 The name is $nvl(name,"defaultValue")$ |
如下例子casef,判断变量与哪个值匹配,便显示响应的值
//java代码 template.set("score",3); //模板 The name is $casef(score,3,"Good",2 ,"so-so",1 ,"bad","error score")$ |
Beetl允许用户自定义函数以适合特定领域使用,请参考高级用法。也欢迎有人把他认为能公用的函数发给我,我将作为核心函数放入beetl里
格式化
几乎所有的模板语言都支持格式化,Beetl也不列外,如下例子Beetl提供的内置日期格式
#:var date = now(); Today is $date,df="yyyy-MM-dd"$. Today is $date,df$ |
如果没有为格式化函数输入参数,则使用默认值,df格式化函数默认值是local
Beetl允许用户自定义格式化函数以适合特定领域,请参考高级用户,也欢迎有人把他认为能公用的格式化函数发给我,我将作为核心函数放入beetl里
错误处理
Beetl默认情况使用 org.bee.tl.core.DefaultErrorHandler 来处理语法解析错误和运行时刻的错误,默认情况下,会显示行号,错误原因,以及错误的关键字
如下所示
BeeTemplate t = new BeeTemplate("#:if(!isGirl){var c=1;}");
|
其他琐碎功能
对齐: 我发现别的模板语言要是做到对齐,非常困难,Beetl你完全不用担心,比如velocty,stringtemlate,freemarker例子都出现了不对齐的情况,影响了美观,Beetl完全无需担心输出对齐
包含其他模板文件:在Beetl中,这不是一个特殊的功能,通过调用函数includeFT,或者includeST都可以实现,前者是包含一个文件模板,后者是将一个string模板作为输入。详细使用请参考高级用法
Escape:可以使用/ 做escape符号,如hello,it's $money$/$, 或者Hello,it's $money+"/$"$
空值策略: 在输出一个变量的时候,如果为null,是应该抛出异常还是仅仅忽略,打印出默认值,Beetl相对于其他模板语言来说,控制的更为精细,如下代码
#:var NULL='N/A';var NULL_POLICY=1; $u.name$ |
第一行指示空值情况下仅仅输出默认值,此时NULL_POLICY=1 表示不抛出异常,而仅仅输出默认值,0为默认情况,即抛出异常,停止渲染
你可以在Beetl中任何一处重新设置这些值以满足特定的需求。
文本处理函数:文本处理函数允许你将模板文件中的一段文件内容作为输入,经过函数操作,变成特定的输出,如下内置的replaceProperties
#:replaceProperties(ip,port){ Server_ip= 127.0.0.1 Server_port= 8080 #:} #:if(isProduct) { delNext(){ Debug_para1=..... Debug_para2=...... #:} |
如果在java代码中,tempalte.set("ip",targetIP),template.set("port",targetPort);
则模板文件里等于号后的字符串将被以此替换.
如果在java代码中,template.set("isProduct",true),则所有debug参数都将被删除。关于文本处理函数概念,请参考高级用法
高级用法
自定义函数
Beetl允许提供自定义函数以适合特定业务需求,自定义函数需要实现org.bee.tl.core.Function。如下定义了一个now函数仅仅返回一个java.util.Date实例
public class DateFunction implements Function { public Object call(Object... paras) { return new Date(); } public static void main(String[] args) throws IOException{ GroupTemplate group = new GroupTemplate(); group.registerFunction("now", new DateFunction()); Template t = group.getStringTemplate("today is $now()$"); System.out.println(t.getTextAsString()); }
} |
格式化函数
Beetl允许提供自定义格式化函数,用于格式化输出。 格式化函数需要实现org.bee.tl.core.Format
public class DateFormat extends Format { public Object format(Object data,String pattern){ SimpleDateFormat sdf = null; if(pattern==null){
sdf = new SimpleDateFormat(); }else{ sdf = new SimpleDateFormat(pattern); }
return sdf.format(data); } public static void main(String[] args)throws IOException { GroupTemplate group = new GroupTemplate(); group.registerFunction("now", new DateFunction()); group.registerFormat("df", new DateFormat()); Template t = group.getStringTemplate("today is $now(),df=’yyyy-MM-dd’$"); System.out.println(t.getTextAsString()); } }
|
严格MVC控制
如果设置了严格MVC,则以下语法将不在模板文件里允许,否则将报出STRICK_MVC 错误
l 定义变量,为变量赋值
l 算术表达式
l 除了只允许布尔以外,不允许逻辑表达式
l 方法调用
虚拟属性
无需为java对象定义额外的属性用于辅助显示,虚拟属性可以轻易做到,如Beetl为java.util.Collection 定义的一个虚拟属性size,用于表示集合大小
group.registerVirtualAttributeEval(new VirtualAttributeEval(){ public Object eval(Object o,String attributeName,Context ctx){ if(attributeName.equals("size")){ return ((Collection)o).size(); }else{ throw new IllegalArgumentException(); }
} public boolean isSuppoert(Class c,String attributeName){ if(Collection.class.isAssignableFrom(c)&&attributeName.equals("size")){ return true; }else{ return false; } } }); |
这样,所以Collection子类都有虚拟属性size。$userList.~size$ 输出userList集合长度
实现虚拟属性,必须实现接口俩个方法,一个是isSupport,这让Beetl用于找到VirtualAttributeEval,eval方法用于计算虚拟属性
文本处理函数
所谓文本处理函数,即允许处理模板文件里的一块内容。如下{}的内容在beetl运行的时候将会被删除
#:del(){ This content will be deleted #:} |
自定义文本处理函数必须实现org.bee.tl.core.TextPorcessFunction,需要实现requriedInput,用于告诉Beetl,是否需要先渲染文本体。
getOutput 用于返回文本处理函数的结果
如下是Beetl提供的内置的del文本处理函数实现
public class DeleteFunction extends TextPorcessFunction{ public String getOutput(){ return ""; } @Override public boolean requriedInput(){ return false; }
}
|
可以通过父类属性args,获取输入参数,详细可以参考API
自定义错误处理