1. 什么是Beetl
Beetl目前版本是2.2.8,相对于其他java模板引擎,具有功能齐全,语法直观,性能超高,以及编写的模板容易维护等特点。使得开发和维护模板有很好的体验。是新一代的模板引擎。总得来说,它的特性如下:
-
功能完备:作为主流模板引擎,Beetl具有相当多的功能和其他模板引擎不具备的功能。适用于*各种应用场景*,从对响应速度有很高要求的大网站到功能繁多的CMS管理系统都适合。Beetl本身还具有很多独特功能来完成模板编写和维护,这是其他模板引擎所不具有的。
-
非常简单:类似Javascript语法和习俗,只要半小时就能通过半学半猜完全掌握用法。拒绝其他模板引擎那种非人性化的语法和习俗。同时也能支持html 标签,使得开发CMS系统比较容易
-
超高的性能:Beetl 远超过主流java模板引擎性能(引擎性能5-6倍与freemaker,2倍于JSP。参考附录),而且消耗较低的CPU
-
易于整合:Beetl能很容易的与各种web框架整合,如Spring MVC,JFinal,Struts,Nutz,Jodd,Servlet等。
-
支持模板单独开发和测试,即在MVC架构中,即使没有M和C部分,也能开发和测试模板。
-
扩展和个性化:Beetl支持自定义方法,格式化函数,虚拟属性,标签,和HTML标签. 同时Beetl也支持自定义占位符和控制语句起始符号也支持使用者完全可以打造适合自己的工具包.
2. 基本用法
2.1. 从GroupTemplate开始
1 2 3 4 5 6 7 |
StringTemplateResourceLoader resourceLoader = new StringTemplateResourceLoader(); Configuration cfg = Configuration.defaultConfiguration(); GroupTemplate gt = new GroupTemplate(resourceLoader, cfg); Template t = gt.getTemplate("hello,${name}"); t.binding("name", "beetl"); String str = t.render(); System.out.println(str);
|
Beetl的核心是GroupTemplate,创建GroupTemplate需要俩个参数,一个是模板资源加载器,一个是配置类,模板资源加载器Beetl内置了4种,分别是
-
StringTemplateResourceLoader:字符串模板加载器,用于加载字符串模板,如本例所示
-
FileResourceLoader:文件模板加载器,需要一个根目录作为参数构造,,传入getTemplate方法的String是模板文件相对于Root目录的相对路径
-
ClasspathResourceLoader:文件模板加载器,模板文件位于Classpath里
-
WebAppResourceLoader:用于webapp集成,假定模板根目录就是WebRoot目录,参考web集成章
-
MapResourceLoader : 可以动态存入模板
代码第5行将变量name传入模板里,其值是“Beetl”。 代码第6行是渲染模板,得到输出,template提供了多种获得渲染输出的方法,如下
-
tempalte.render() 返回渲染结果,如本例所示
-
template.renderTo(Writer) 渲染结果输出到Writer里
-
template.renderTo(OutputStream ) 渲染结果输出到OutputStream里
2.2. 模板基础配置
Beetl提供不但功能齐全,而且还有很多独特功能,通过简单的配置文件,就可以定义众多的功能,默认情况下,Configuration类总是会先加载默认的配置文件(位于/org/beetl/core/beetl-default.properties,作为新手,通常只需要关注3,4,5,6行定界符的配置,以及11行模板字符集的配置就可以了,其他配置会在后面章节陆续提到)下,其内容片断如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#默认配置
ENGINE=org.beetl.core.engine.FastRuntimeEngine
DELIMITER_PLACEHOLDER_START=${
DELIMITER_PLACEHOLDER_END=}
DELIMITER_STATEMENT_START=<%
DELIMITER_STATEMENT_END=%>
DIRECT_BYTE_OUTPUT = FALSE
HTML_TAG_SUPPORT = true
HTML_TAG_FLAG = #
HTML_TAG_BINDING_ATTRIBUTE = var
NATIVE_CALL = TRUE
TEMPLATE_CHARSET = UTF-8
ERROR_HANDLER = org.beetl.core.ConsoleErrorHandler
NATIVE_SECUARTY_MANAGER= org.beetl.core.DefaultNativeSecurityManager
MVC_STRICT = FALSE
\#资源配置,resource后的属性只限于特定ResourceLoader
RESOURCE_LOADER=org.beetl.core.resource.ClasspathResourceLoader
#classpath 根路径
RESOURCE.root= /
#是否检测文件变化
RESOURCE.autoCheck= true
#自定义脚本方法文件的Root目录和后缀
RESOURCE.functionRoot = functions
RESOURCE.functionSuffix = html
#自定义标签文件Root目录和后缀
RESOURCE.tagRoot = htmltag
RESOURCE.tagSuffix = tag
##### 扩展 ##############
## 内置的方法
FN.date = org.beetl.ext.fn.DateFunction
......
##内置的功能包
FNP.strutil = org.beetl.ext.fn.StringUtil
......
##内置的默认格式化函数
FTC.java.util.Date = org.beetl.ext.format.DateFormat
.....
## 标签类
TAG.include= org.beetl.ext.tag.IncludeTag
|
第2行配置引擎实现类,默认即可.
第3,4行指定了占位符号,默认是${ }.也可以指定为其他占位符。
第5,6行指定了语句的定界符号,默认是<% %>,也可以指定为其他定界符号
第7行指定IO输出模式,默认是FALSE,即通常的字符输出,在考虑高性能情况下,可以设置成true。详细请参考高级用法
第8,9行指定了支持HTML标签,且符号为#,默认配置下,模板引擎识别<#tag ></#tag>这样的类似html标签,并能调用相应的标签函数或者模板文件。你也可以指定别的符号,如bg: 则识别<bg:
第10行 指定如果标签属性有var,则认为是需要绑定变量给模板的标签函数
第11行指定允许本地Class直接调用
第12行指定模板字符集是UTF-8
第13行指定异常的解析类,默认是ConsoleErrorHandler,他将在render发生异常的时候在后台打印出错误信息(System.out)。
第14行指定了本地Class调用的安全策略
第15行配置了是否进行严格MVC,通常情况下,此处设置为false.
第18行指定了默认使用的模板资源加载器
第20到22行配置了模板资源加载器的一些属性,如设置根路径为/,即Classpath的顶级路径,并且总是检测模板是否更改
第23行配置了自定义的方法所在的目录以及文件名后缀。beetl既支持通过java类定义方法,也支持通过模板文件来定义方法
第26行配置了自定义的html标签所在的目录以及文件名后缀。beetl既支持通过java类定义标签,也支持通过模板文件来定义标签
第31行注册了一个date方法,其实现类是org.beetl.ext.fn.DateFunction
第34行注册了一个方法包strutil,其实现类org.beetl.ext.fn.StringUtil,此类的每个public方法都将注册为beetl的方法
第37行注册了一个日期格式化函数
第40行注册了一个include标签函数
2.3. 模板资源加载器
资源加载器是根据String值获取Resource实例的工场类,同时资源加载器还要负责响应模板引擎询问模板是否变化的调用。对于新手来说,无需考虑模板资源加载器如何实现,只需要根据自己场景选择系统提供的三类模板资源加载器即可
2.3.1. 字符串模板加载器
在创建GroupTemplate过程中,如果传入的是StringTemplateResourceLoader,则允许通过调用gt.getTemplate(String template)来获取模板实例对象,如2.1所示
2.3.2. 文件资源模板加载器
更通常情况下,模板资源是以文件形式管理的,集中放在某一个文件目录下(如webapp的模板根目录就可能是WEB-INF/template里),因此,可以使用FileResourceLoader来加载模板实例,如下代码:
1 2 3 4 5 6 7 |
String root = System.getProperty("user.dir")+File.separator+"template"; FileResourceLoader resourceLoader = new FileResourceLoader(root,"utf-8"); Configuration cfg = Configuration.defaultConfiguration(); GroupTemplate gt = new GroupTemplate(resourceLoader, cfg); Template t = gt.getTemplate("/s01/hello.txt"); String str = t.render(); System.out.println(str);
|
第1行代码指定了模板根目录,即位于项目工程下的template目录 第2行构造了一个资源加载器,并指定字符集为UTF-8 (也可不指定,因为配置文件默认就是UTF-8); 第5行通过模板的相对路径/s01/hello.txt来加载模板
2.3.3. Classpath资源模板加载器
还有种常情况下,模板资源是打包到jar文件或者同Class放在一起,因此,可以使用ClasspathResourceLoader来加载模板实例,如下代码:
1 2 3 4 5 6 |
ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader(); Configuration cfg = Configuration.defaultConfiguration(); GroupTemplate gt = new GroupTemplate(resourceLoader, cfg); Template t = gt.getTemplate("/org/beetl/sample/s01/hello.txt"); String str = t.render(); System.out.println(str);
|
第1行代码指定了模板根目录,即搜索模板的时候从根目录开始,如果new ClasspathResourceLoader("/template"),则表示搜索/template下的模板。此处用空构造函数,表示搜索路径是根路径,且字符集默认字符集UTF-8.
第4行通过模板的相对路径org/beetl/sample/s01/hello.txt来加载模板
2.3.4. WebApp资源模板加载器
WebAppResourceLoader 是用于web应用的资源模板加载器,默认根路径是WebRoot目录。也可以通过制定root属性来设置相对于WebRoot的的模板根路径,从安全角考虑,建议放到WEB-INF目录下
如下是Jfinal集成 里初始化GroupTemplate的方法
1 2 3 |
Configuration cfg = Configuration.defaultConfiguration(); WebAppResourceLoader resourceLoader = new WebAppResourceLoader(); groupTemplate = new GroupTemplate(resourceLoader, cfg);
|
2.3.5. 自定义资源模板加载器
有时候模板可能来自文件系统不同目录,或者模板一部分来自某个文件系统,另外一部分来自数据库,还有的情况模板可能是加密混淆的模板,此时需要自定义资源加载,继承ResouceLoader才能实现模板功能,这部分请参考高级部分
2.4. 定界符与占位符号
Beetl模板语言类似JS语言和习俗,只需要将Beetl语言放入定界符号里即可,如默认的是<% %> ,占位符用于静态文本里嵌入占位符用于输出,如下是正确例子
1 2 3 4 5 6 |
<%
var a = 2; var b = 3; var result = a+b; %> hello 2+3=${result}
|
千万不要在定界符里使用占位符号,因为占位符仅仅嵌在静态文本里,如下例子是错误例子
1 2 3 4 |
<%
var a = "hi"; var c = ${a}+"beetl"; //应该是var c = a+"beetl" %>
|
每次有人问我如上例子为啥不能运行的时候,我总是有点憎恶velocity 带来的这种非人性语法
定界符号里是表达式,如果表达式跟定界符有冲突,可以在表达式里用 “\” 符号,如
1 2 |
${[1,2,3]} //输出一个json列表 ${ {key:1,value:2 \} } //输出一个json map,} 需要加上\
|
定界符和占位符 通常还有别的选择,如下定界符
-
@ 和回车换行 (此时,模板配置DELIMITER_STATEMENT_END= 或者 DELIMITER_STATEMENT_END=null 都可以)
-
#: 和回车换行
-
<!--: 和 -→
-
<!--# 和 -→
-
<? 和 ?>
占位符: - - #{ } - # #
你也可以与团队达成一致意见来选择团队喜爱择定界符号和占位符号。
2.5. 注释
Beetl语法类似js语法,所以注释上也同js一样: 单行注释采用//
多行注视采用/**/
1 2 3 4 5 6 7 8 |
<%
/*此处是一个定义变量*/ var a = 3; //定义一个变量. /* 以下内容都将被注释 %> <% */ %>
|
第2行是一个多行注释
第3行是一个单行注释
第5行到第8行采用的是多行注释,因此里面有内容也是注释,模板将不予处理
2.6. 临时变量定义
在模板中定义的变量成为临时变量,这类似js中采用var 定义的变量,如下例子
1 2 3 4 5 6 7 8 |
<%
var a = 3; var b = 3,c = "abc",d=true,e=null; var f = [1,2,3]; var g = {key1:a,key2:c}; var i = a+b; %>
|
2.7. 全局变量定义
全局变量是通过template.binding传入的变量,这些变量能在模板的任何一个地方,包括子模板都能访问到。如java代码里
1 2 3 4 5 6 7 8 |
template.binding("list",service.getUserList()); //在模板里 <% for(user in list){ %> hello,${user.name}; <%}%>
|
2.8. 共享变量
共享变量指在所有模板中都可以引用的变量,可过groupTemplate.setSharedVars(Map<String, Object> sharedVars)传入的变量,这些变量能在 所有模板 的任何一个地方
1 2 3 4 5 6 7 8 9 10 11 |
.....
GroupTemplate gt = new GroupTemplate(resourceLoader, cfg); Map<String,Object> shared = new HashMap<String,Object>(); shared.put("name", "beetl"); gt.setSharedVars(shared); Template t = gt.getTemplate("/org/beetl/sample/s0208/t1.txt"); String str = t.render(); System.out.println(str); t = gt.getTemplate("/org/beetl/sample/s0208/t2.txt"); str = t.render(); System.out.println(str);
|
1 2 3 4 |
//t1.txt
hi,${name} //t2.txt hello,${name}
|
2.9. 模板变量
模板变量是一种特殊的变量,即可以将模板中任何一段的输出赋值到该变量,并允许稍后在其他地方使用,如下代码
1 2 3 4 5 6 7 8 |
<%
var content = { var c = "1234"; print(c); %> 模板其他内容: <%}; %>
|
第2行定义了一个模板变量content = { …} ; 此变量跟临时变量一样,可以在其他地方使用,最常见的用户是用于复杂的布局。请参考高级用法布局
2.10. 引用属性
属性引用是模板中的重要一部分,beetl支持属性引用如果javascript的支持方式一样,如下
1 Beetl支持通过”.”号来访问对象的的属性,如果javascript一样。如果User对象有个getName()方法,那么在模板中,可以通过${xxx.name}来访问
2 如果模板变量是数组或者List类,这可以通过[] 来访问,如${userList[0]}
3 如果模板变量是Map类,这可以通过[]来访问,如${map[“name”]},如果key值是字符串类型,也可以使用${map.name}.但不建议这么使用,因为会让模板阅读者误以为是一个Pojo对象
4 Beetl也支持Generic Get方式,即如果对象有一个public Object get(String key)方法,可以通过”.”号或者[]来访问,譬如 ${activityRecord.name}或者${activityRecord[“name”] }都将调用activityRecord的 get(String key)方法。如果对象既有具体属性,又有Generic get(这种模型设计方式是不值得鼓励),则以具体属性优先级高.
5 Beetl也可以通过[]来引用属性,如${user[“name”]} 相当于${user.name}.这跟javascript保持一致。但建议不这么做,因为容易让阅读模板的人误认为这是一个Map类型
6 Beetl 还可以定位额外的对象属性,而无需更改java对象,这叫着虚拟属性,如,对于所有集合,数组,都有共同的虚拟熟悉size.虚拟属性是“.~”+虚拟1属性名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
template.binding("list",service.getUserList()); template.binding("pageMap",service.getPage()); //在模板里 总共 ${list.~size} <% for(user in list){ %> hello,${user.name}; <%}%> 当前页${pageMap['page']},总共${pageMap["total"]}
|
2.11. 算数表达式
Beetl支持类似javascript的算术表达式和条件表达式,如+ - * / % 以及(),以及自增++,自减--
1 2 3 4 5 6 7 8 |
<%
var a = 1; var b = "hi"; var c = a++; var d = a+100.232; var e = (d+12)*a; var f = 122228833330322.1112h %>
|
Beetl里定义的临时变量类型默认对应的java是Int型或者double类型,对于模板常用情况说,已经够了.如果需要定义长精度类型(对应java的BigDecimal),则需要在数字末尾加上h以表示这是长精度BigDecimal,其后的计算和输出以及逻辑表达式都将按照长精度类型来考虑。
2.12. 逻辑表达式
Beetl支持类似Javascript,java的条件表达式 如>, <, == ,!=,>= , ⇐ 以及 !, 还有&&和 || ,还有三元表达式等,如下例子
1 2 3 4 5 6 7 8 9 |
<%
var a = 1; var b=="good"; var c = null; if(a!=1&&b=="good"&&c==null){ ...... } %>
|
三元表达式如果只考虑true条件对应的值的话,可以做简化,如下俩行效果是一样的。
1 2 3 4 5 |
<%
var a = 1 ; %> ${a==1?"ok":''} ${a==1?"ok"}
|
2.13. 循环语句
Beetl支持丰富的循环方式,如for-in,for(exp;exp;exp),以及while循环,以及循环控制语句break;continue; 另外,如果没有进入for循环体,还可以执行elsefor指定的语句。
2.13.1. for-in
for-in循环支持遍历集合对象,对于List和数组来说以及Iterator,对象就是集合对象,对于Map来说,对象就是Map.entry,如下俩个例子
1 2 3 4 5 6 7 |
<%
for(user in userList){ print(userLP.index); print(user.name); } %>
|
第三行代码userLP是Beetl隐含定义的变量,能在循环体内使用。其命名规范是item名称后加上LP,他提供了当前循环的信息,如
-
userLP.index :当前的索引,从1开始
-
userLP.size:集合的长度
-
userLP.first 是否是第一个
-
userLP.last 是否是最后一个
-
userLP.even 索引是否是偶数
-
userLP.odd 索引是否是奇数
如下是Map使用例子
1 2 3 4 5 6 7 8 |
<%
for(entry in map){ var key = entry.key; var value = entry.value; print(value.name); } %>
|
2.13.2. for(exp;exp;exp)
对于渲染逻辑更为常见的是经典的for循环语句,如下例子
1 2 3 4 5 6 |
<%
var a = [1,2,3]; for(var i=0;i<a.~size;i++){ print(a[i]); } %>
|
2.13.3. while
对于渲染逻辑同样常见的有的while循环语句,如下例子
1 2 3 4 5 6 7 |
<%
var i = 0; while(i<5){ print(i); i++; } %>
|
2.13.4. elsefor
不同于通常程序语言,如果没有进入循环体,则不需额外的处理,模板渲染逻辑更常见情况是如果没有进入循环体,还需要做点什么,因此,对于for循环来说,还有elsefor 用来表达如果循环体没有进入,则执行elsefor 后的语句
1 2 3 4 5 6 7 8 |
<%
var list = []; for(item in list){ }elsefor{ print("未有记录"); } %>
|
2.14. 条件语句
2.14.1. if else
同js一样,支持if else,如下例子
1 2 3 4 5 6 7 8 9 10 11 12 |
<%
var a =true; var b = 1; if(a&&b==1){ }else if(a){ }else{ } %>
|
2.14.2. switch-case
同js一样,支持switch-case,如下例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<%
var b = 1; switch(b){ case 0: print("it's 0"); break; case 1: print("it's 1"); break; default: print("error"); } %>
|
2.14.3. select-case
select-case 是switch case的增强版。他允许case 里有逻辑表达式,同时,也不需要每个case都break一下,默认遇到合乎条件的case执行后就退出。
1 2 3 4 5 6 7 8 9 10 11 12 |
<%
var b = 1; select(b){ case 0,1: print("it's small int"); case 2,3: print("it's big int"); default: print("error"); } %>
|
select 后也不需要一个变量,这样case 后的逻辑表达式将决定执行哪个case.其格式是
1 2 3 4 5 6 |
select { case boolExp,orBoolExp2: doSomething(); } %>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<%
var b = 1; select{ case b<1,b>10: print("it's out of range"); break; case b==1: print("it's 1"); break; default: print("error"); } %>
|
2.15. try-catch
通常模板渲染逻辑很少用到try-catch 但考虑到渲染逻辑复杂性,以及模板也有不可控的地方,所以提供try catch,在渲染失败的时候仍然能保证输出正常
1 2 3 4 5 6 7 8 |
<%
try{ callOtherSystemView() }catch(error){ print("暂时无数据"); } %>
|
error代表了一个异常,你可以通过error.message 来获取可能的错误信息
也可以省略catch部分,这样出现异常,不做任何操作
2.16. 虚拟属性
虚拟属性也是对象的属性,但是虚拟的,非模型对象的真实属性,这样的好处是当模板需要额外的用于显示的属性的时候但又不想更改模型,便可以采用这种办法 如beetl内置的虚拟属性.~size 针对了数组以及集合类型。
1 2 |
${user.gender} ${user.~genderShowName}
|
~genderShowName 是虚拟属性,其内部实现根据boolean变量gender来显示性别
如何完成虚拟属性,请参考高级用法
2.17. 函数调用
Beetl内置了少量实用函数,可以在Beetl任何地方调用。如下例子是调用date 函数,不传参数情况下,返回当前日期
1 2 3 4 5 6 |
<%
var date = date(); var len = strutil.length("cbd"); println("len="+len); %>
|
注意函数名支持namespace方式,因此代码第3行调用的函数是strutil.length
Beetl内置函数请参考附录,以下列出了常用的函数
-
date 返回一个java.util.Date类型的变量,如 date() 返回一个当前时间(对应java的java.util.Date); ${date( "2011-1-1" , "yyyy-MM-dd" )} 返回指定日期
-
print 打印一个对象 print(user.name);
-
println 打印一个对象以及回车换行符号,回车换号符号使用的是模板本身的,而不是本地系统的.如果仅仅打印一个换行符,则直接调用println() 即可
-
nvl 函数nvl,如果对象为null,则返回第二个参数,否则,返回自己 nvl(user,"不存在")
-
isEmpty 判断变量或者表达式是否为空,变量不存在,变量为null,变量是空字符串,变量是空集合,变量是空数组,此函数都将返回true
-
isNotEmpty 同上,判断对象是否不为空
-
has 变量名为参数,判断是否存在此全局变量,如 has(userList),类似于1.x版本的exist("userList"),但不需要输入引号了
-
assert 如果表达式为false,则抛出异常
-
trunc 截取数字,保留指定的小数位,如trunc(12.456,2) 输出是12.45
-
decode 一个简化的if else 结构,如 decode(a,1,"a=1",2,"a=2","不知道了")},如果a是1,这decode输出"a=1",如果a是2,则输出"a==2", 如果是其他值,则输出"不知道了"
-
debug 在控制台输出debug指定的对象以及所在模板文件以及模板中的行数,如debug(1),则输出1 [在3行@/org/beetl/core/lab/hello.txt],也可以输出多个,如debug("hi",a),则输出hi,a=123,[在3行@/org/beetl/core/lab/hello.txt]
-
parseInt 将数字或者字符解析为整形 如 parseInt("123");
-
parseLong 将数字或者字符解析为长整形,parseInt(123.12);
-
parseDouble 将数字或者字符解析为浮点类型 如parseDouble("1.23")
-
range 接收三个参数,初始值,结束值,还有步增(可以不需要,则默认为1),返回一个Iterator,常用于循环中,如for(var i in range(1,5)) {print(i)},将依次打印1234.
-
flush 强制io输出。
-
json,将对象转成json字符串,如 var data = json(userList) 可以跟一个序列化规则 如,var data = json(userList,"[*].id:i"),具体参考https://git.oschina.net/xiandafu/beetl-json
-
pageCtx ,仅仅在web开发中,设置一个变量,然后可以在页面渲染过程中,调用此api获取,如pageCtx("title","用户添加页面"),在其后任何地方,可以pageCtx("title") 获取该变量
2.18. 安全输出
安全输出是任何一个模板引擎必须重视的问题,否则,将极大困扰模板开发者。Beetl中,如果要输出的模板变量为null,则beetl将不做输出,这点不同于JSP,JSP输出null,也不同于Feemarker,如果没有用!,它会报错.
模板中还有俩种情况会导致模板输出异常
-
有时候模板变量并不存在(譬如子模板里)
-
模板变量为null,但输出的是此变量的一个属性,如${user.wife.name}
针对前俩种种情况,可以在变量引用后加上!以提醒beetl这是一个安全输出的变量。
如${{user.wife.name! },即使user不存在,或者user为null,或者user.wife为null,或者user.wife.name为null beetl都不将输出
可以在!后增加一个常量(字符串,数字类型等),或者另外一个变量,方法,本地调用,作为默认输出,譬如:
${user.wife.name!”单身”},如果user为null,或者user.wife为null,或者user.wife.name为null,输出”单身”
譬如
${[email protected]}, 表示如果user为null,或者user. birthday为null,输出System.constants.DefaultBir
还有一种情况很少发生,但也有可能,输出模板变量发生的任何异常,如变量内部抛出的一个异常
这需要使用格式${!(变量)},这样,在变量引用发生任何异常情况下,都不作输出,譬如
${!(user.name)},,beetl将会调用user.getName()方法,如果发生异常,beetl将会忽略此异常,继续渲染
值得注意的是,在变量后加上!不仅仅可以应用于占位符输出(但主要是应用于占位符输出),也可以用于表达式中,如:
1 2 3 4 5 6 7 8 |
<%
<% var k = user.name!'N/A'+user.age!; %> ${k} %>
|
如果user为null,则k值将为N/A
在有些模板里,可能整个模板都需要安全输出,也可能模板的部分需要安全输出,使用者不必为每一个表达式使用!,可以使用beetl的安全指示符号来完成安全输出 如:
1 2 3 4 5 6 7 8 9 |
<%
DIRECTIVE SAFE_OUTPUT_OPEN; %> ${user.wife.name} 模板其他内容,均能安全输出…… <% //关闭安全输出。 DIRECTIVE SAFE_OUTPUT_CLOSE; %>
|
Beetl不建议每一个页面都使用DIRECTIVE SAFE_OUTPUT_OPEN,这样,如果如果真有不期望的错误,不容易及时发现,其次,安全输出意味着beetl会有额外的代码检测值是否存在或者是否为null,性能会略差点。所以建议及时关闭安全输出(这不是必须的,但页面所有地方是安全输出,可能不容易发现错误)
在for-in 循环中 ,也可以为集合变量增加安全输出指示符号,这样,如果集合变量为null,也可以不进入循环体,如:
1 2 3 4 5 6 7 8 |
<%
var list = null; for(item in list!){ }eslefor{ print("no data"); } %>
|
2.18.1. 变量是否存在
判断变量是否存在,可以采用内置的has或者isEmpty方法来判断,参数是变量,如
1 2 3 4 5 |
<%
if(has(flag)){ print("not exit") } %>
|
如果需要判断变量是否存在,如果存在,还有其他判断条件,通常都这么写
1 2 3 4 5 |
<%
if(has(flag)||flag==0){ //code } %>
|
如果flag不存在,或者flag存在,但值是0,都将执行if语句
但是,有更为简便的方法是直接用安全输出,如
1 2 3 4 5 |
<%
if(flag!0==0){ //code } %>
|
flag!0 取值是这样的,如果flag不存在,则为0,如果存在,则取值flag的值,类似三元表达式 has(flag)?falg:0
2.18.2. 安全输出表达式
安全输出表达式可以包括
-
字符串常量,如 ${user.count!"无结果"}
-
boolean常量 ${user.count!false}
-
数字常量,仅限于正数,因为如果是负数,则类似减号,容易误用,因此,如果需要表示负数,请用括号,如${user.count!(-1)}
-
class直接调用,如${[email protected]_NUM}
-
方法调用,如 ${user.count!getDefault() }
-
属性引用,如 ${user.count!user.maxCount }
-
任何表达式,需要用括号
2.19. 格式化
几乎所有的模板语言都支持格式化,Beetl也不列外,如下例子Beetl提供的内置日期格式
1 2 3 4 |
<% var date = date(); %> Today is ${date,dateFormat="yyyy-MM-dd"}. Today is ${date,dateFormat} salary is ${salary,numberFormat="##.##"}
|
格式化函数只需要一个字符串作为参数放在=号后面,如果没有为格式化函数输入参数,则使用默认值,dateFormat格式化函数默认值是local
Beetl也允许为指定的java class设定格式化函数,譬如已经内置了对java.util.Date,java.sql.Date 设置了了格式化函数,因此上面的例子可以简化为
1 |
${date,“yyyy-MM-dd”}.
|
Beetl针对日期和数字类型提供的默认的格式化函数,在org/beetl/core/beetl-default.properties里,注册了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
##内置的格式化函数 FT.dateFormat = org.beetl.ext.format.DateFormat FT.numberFormat = org.beetl.ext.format.NumberFormat ##内置的默认格式化函数 FTC.java.util.Date = org.beetl.ext.format.DateFormat FTC.java.sql.Date = org.beetl.ext.format.DateFormat FTC.java.sql.Time = org.beetl.ext.format.DateFormat FTC.java.sql.Timestamp = org.beetl.ext.format.DateFormat FTC.java.lang.Short = org.beetl.ext.format.NumberFormat FTC.java.lang.Long = org.beetl.ext.format.NumberFormat FTC.java.lang.Integer = org.beetl.ext.format.NumberFormat FTC.java.lang.Float = org.beetl.ext.format.NumberFormat FTC.java.lang.Double = org.beetl.ext.format.NumberFormat FTC.java.math.BigInteger = org.beetl.ext.format.NumberFormat FTC.java.math.BigDecimal = org.beetl.ext.format.NumberFormat FTC.java.util.concurrent.atomic.AtomicLong = org.beetl.ext. |