Beetl學習總結(2)——基本用法

2.1. 安裝
若是使用maven,使用以下座標
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>2.7.0</version>
</dependency>
若是非maven工程,直接下載 http://git.oschina.net/xiandafu/beetl2.0/attach_files

2.2. 從GroupTemplate開始
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內置了6種,分別是
StringTemplateResourceLoader:字符串模板加載器,用於加載字符串模板,如本例所示
FileResourceLoader:文件模板加載器,須要一個根目錄做爲參數構造,,傳入getTemplate方法的String是模板文件相對於Root目錄的相對路徑
ClasspathResourceLoader:文件模板加載器,模板文件位於Classpath裏
WebAppResourceLoader:用於webapp集成,假定模板根目錄就是WebRoot目錄,參考web集成章
MapResourceLoader : 能夠動態存入模板
CompositeResourceLoader 混合使用多種加載方式
代碼第5行將變量name傳入模板裏,其值是「Beetl」。 代碼第6行是渲染模板,獲得輸出,template提供了多種得到渲染輸出的方法,以下
tempalte.render() 返回渲染結果,如本例所示
template.renderTo(Writer) 渲染結果輸出到Writer裏
template.renderTo(OutputStream ) 渲染結果輸出到OutputStream裏
關於如何使用模板資源加載器,請參考下一節
如何對模板進行配置,請參考下一節

2.3. 模板基礎配置
Beetl提供不但功能齊全,並且還有不少獨特功能,經過簡單的配置文件,就能夠定義衆多的功能,默認狀況下,Configuration類老是會先加載默認的配置文件(位於/org/beetl/core/beetl-default.properties,做爲新手,一般只須要關注3,4,5,6行定界符的配置,以及12行模板字符集的配置就能夠了,其餘配置會在後面章節陸續提到)下,其內容片段以下:
#默認配置
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標籤函數
模板開發者能夠建立一個beetl.properties的配置文件,此時,該配置文件將覆蓋默認的配置文件屬性,好比,你的定界符考慮是<!--: 和 -→ ,則在beetl.properties加入一行便可,並將此配置文件放入Classpath根目錄下便可。 Configuration.defaultConfiguration()老是先加載系統默認的,而後再加載Beetl.properties的配置屬性,若是有重複,用後者代替前者的配置
#自定義配置
DELIMITER_STATEMENT_START=<!--:
DELIMITER_STATEMENT_END=-->
2.4.0 新功能:beetl 支持經過模板本生來完成函數,即模板函數,或者經過模板來實現HTML標籤(而不用寫java代碼),能夠beetl.properties爲這種應用設置的不一樣的語句定界符來跟常規模板作區分,以下
FUNCTION_TAG_LIMITER=<%;%>
分號分割開,若是配置文件沒有FUNCTION_TAG_LIMITER=,則模板函數,html標籤使用同DELIMITER_STATEMENT_START,DELIMITER_STATEMENT_END
2.4. 模板資源加載器
資源加載器是根據String值獲取Resource實例的工場類,同時資源加載器還要負責響應模板引擎詢問模板是否變化的調用。對於新手來講,無需考慮模板資源加載器如何實現,只須要根據本身場景選擇系統提供的三類模板資源加載器便可
2.4.1. 字符串模板加載器

在建立GroupTemplate過程當中,若是傳入的是StringTemplateResourceLoader,則容許經過調用gt.getTemplate(String template)來獲取模板實例對象,如2.1所示

2.4.2. 文件資源模板加載器

更一般狀況下,模板資源是以文件形式管理的,集中放在某一個文件目錄下(如webapp的模板根目錄就多是WEB-INF/template裏),所以,可使用FileResourceLoader來加載模板實例,以下代碼:

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.4.3. Classpath資源模板加載器

還有種常狀況下,模板資源是打包到jar文件或者同Class放在一塊兒,所以,可使用ClasspathResourceLoader來加載模板實例,以下代碼:

ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader("org/beetl/sample/s01/」);
Configuration cfg = Configuration.defaultConfiguration(「);
GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
Template t = gt.getTemplate("/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.4.4. WebApp資源模板加載器

WebAppResourceLoader 是用於web應用的資源模板加載器,默認根路徑是WebRoot目錄。也能夠經過制定root屬性來設置相對於WebRoot的的模板根路徑,從安全角考慮,建議放到WEB-INF目錄下

以下是Jfinal集成 裏初始化GroupTemplate的方法

Configuration cfg = Configuration.defaultConfiguration();
WebAppResourceLoader resourceLoader = new WebAppResourceLoader();
groupTemplate = new GroupTemplate(resourceLoader, cfg);
WebAppResourceLoader 假定 beetl.jar 是位於 WEB-INF/lib 目錄下,所以,能夠經過WebAppResourceLoader類的路徑來推斷出WebRoot路徑從而指定模板根路徑。全部線上環境通常都是如此,若是是開發環境或者其餘環境不符合此假設,你須要調用resourceLoader.setRoot() 來指定模板更路徑

2.4.5. 自定義資源模板加載器

有時候模板可能來自文件系統不一樣目錄,或者模板一部分來自某個文件系統,另一部分來自數據庫,還有的狀況模板多是加密混淆的模板,此時須要自定義資源加載,繼承ResouceLoader才能實現模板功能,這部分請參考高級部分

2.5. 定界符與佔位符號
Beetl模板語言相似JS語言和習俗,只須要將Beetl語言放入定界符號裏便可,如默認的是<% %> ,佔位符用於靜態文本里嵌入佔位符用於輸出,以下是正確例子
<%
var a = 2;
var b = 3;
var result = a+b;
%>
hello 2+3=${result}
千萬不要在定界符裏使用佔位符號,由於佔位符僅僅嵌在靜態文本里,以下例子是錯誤例子

<%
var a = "hi";
var c = ${a}+"beetl"; //應該是var c = a+"beetl"
%>
每次有人問我如上例子爲啥不能運行的時候,我老是有點憎惡velocity 帶來的這種非人性語法

定界符和佔位符 一般還有別的選擇,以下定界符

@ 和回車換行 (此時,模板配置DELIMITER_STATEMENT_END= 或者 DELIMITER_STATEMENT_END=null 均可以)

#: 和回車換行

<!--: 和 -→

<!--# 和 -→

<? 和 ?>

佔位符: - - #{ } - # #

你也能夠與團隊達成一致意見來選擇團隊喜好擇定界符號和佔位符號。

定界符號裏是表達式,若是表達式跟定界符或者佔位符有衝突,能夠在用 「\」 符號,如

@for(user in users){
email is ${user.name}\@163.com
@}

${[1,2,3]} //輸出一個json列表
${ {key:1,value:2 \} } //輸出一個json map,} 須要加上\
2.6. 註釋
Beetl語法相似js語法,因此註釋上也同js同樣: 單行註釋採用//

多行注視採用/**/

<%
/*此處是一個定義變量*/
var a = 3; //定義一個變量.

/* 如下內容都將被註釋
%>

<% */ %>
第2行是一個多行註釋

第3行是一個單行註釋

第5行到第8行採用的是多行註釋,所以裏面有內容也是註釋,模板將不予處理

2.7. 臨時變量定義
在模板中定義的變量成爲臨時變量,這相似js中採用var 定義的變量,以下例子

<%

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.8. 全局變量定義
全局變量是經過template.binding傳入的變量,這些變量能在模板的任何一個地方,包括子模板都能訪問到。如java代碼裏

template.binding("list",service.getUserList());

//在模板裏
<%
for(user in list){
%>
hello,${user.name};
<%}%>
2.9. 共享變量
共享變量指在全部模板中均可以引用的變量,可過groupTemplate.setSharedVars(Map<String, Object> sharedVars)傳入的變量,這些變量能在 全部模板 的任何一個地方
.....
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);

//t1.txt
hi,${name}
//t2.txt
hello,${name}
2.10. 模板變量
模板變量是一種特殊的變量,便可以將模板中任何一段的輸出賦值到該變量,並容許稍後在其餘地方使用,以下代碼

<%
var content = {
var c = "1234";
print(c);
%>
模板其餘內容:

<%}; %>
第2行定義了一個模板變量content = { …} ; 此變量跟臨時變量同樣,能夠在其餘地方使用,最多見的用戶是用於複雜的佈局。請參考高級用法佈局

2.11. 引用屬性
屬性引用是模板中的重要一部分,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屬性名

template.binding("list",service.getUserList());
template.binding("pageMap",service.getPage());

//在模板裏
總共 ${list.~size}
<%
for(user in list){
%>
hello,${user.name};


<%}%>

當前頁${pageMap['page']},總共${pageMap["total"]}
2.12. 屬性賦值
Beetl2.7.0 開始支持對象賦值,如

<%
var user = ....
user.name="joelli";
user.friends[0] = getNewUser();
user.map["name"] = "joelli";

%>
2.13. 算數表達式
Beetl支持相似javascript的算術表達式和條件表達式,如+ - * / % 以及(),以及自增++,自減--

<%
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.14. 邏輯表達式
Beetl支持相似Javascript,java的條件表達式 如>, <, == ,!=,>= , ⇐ 以及 !, 還有&&和 || ,還有三元表達式等,以下例子

<%
var a = 1;
var b=="good";
var c = null;

if(a!=1&&b=="good"&&c==null){
......
}
%>
三元表達式若是隻考慮true條件對應的值的話,能夠作簡化,以下倆行效果是同樣的。

<%
var a = 1 ;
%>
${a==1?"ok":''}
${a==1?"ok"}
2.15. 循環語句
Beetl支持豐富的循環方式,如for-in,for(exp;exp;exp),以及while循環,以及循環控制語句break;continue; 另外,若是沒有進入for循環體,還能夠執行elsefor指定的語句。

2.15.1. for-in

for-in循環支持遍歷集合對象,對於List和數組來講以及Iterator,對象就是集合對象,對於Map來講,對象就是Map.entry,以下倆個例子

<%
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 索引是不是奇數

如何記住後綴是LP,有倆個訣竅,英語棒的是Loop的縮寫,拼音好的是老婆的拼音縮寫,這可讓程序員每次寫到這的時候都會想一想老婆(無論有沒有,哈哈)

以下是Map使用例子

<%
for(entry in map){
var key = entry.key;
var value = entry.value;
print(value.name);

}
%>
2.15.2. for(exp;exp;exp)

對於渲染邏輯更爲常見的是經典的for循環語句,以下例子
<%
var a = [1,2,3];
for(var i=0;i<a.~size;i++){
print(a[i]);
}
%>
2.15.3. while

對於渲染邏輯一樣常見的有的while循環語句,以下例子

<%
var i = 0;
while(i<5){
print(i);
i++;
}
%>
2.15.4. elsefor

不一樣於一般程序語言,若是沒有進入循環體,則不需額外的處理,模板渲染邏輯更常見狀況是若是沒有進入循環體,還須要作點什麼,所以,對於for循環來講,還有elsefor 用來表達若是循環體沒有進入,則執行elsefor 後的語句

<%
var list = [];
for(item in list){

}elsefor{
print("未有記錄");
}
%>
2.16. 條件語句
2.16.1. if else

同js同樣,支持if else,以下例子

<%
var a =true;
var b = 1;
if(a&&b==1){

}else if(a){

}else{

}

%>
2.16.2. switch-case

同js同樣,支持switch-case,以下例子

<%
var b = 1;
switch(b){
case 0:
print("it's 0");
break;
case 1:
print("it's 1");
break;
default:
print("error");

}
%>
switch變量能夠支持任何類型,而不像js那樣只能是整形

2.16.3. select-case

select-case 是switch case的加強版。他容許case 裏有邏輯表達式,同時,也不須要每一個case都break一下,默認遇到合乎條件的case執行後就退出。

<%
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.其格式是

select {
case boolExp,orBoolExp2:
doSomething();

}
%>

<%
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.17. try-catch
一般模板渲染邏輯不多用到try-catch 但考慮到渲染邏輯複雜性,以及模板也有不可控的地方,因此提供try catch,在渲染失敗的時候仍然能保證輸出正常


<%
try{
callOtherSystemView()
}catch(error){
print("暫時無數據");
}

%>
error表明了一個異常,你能夠經過error.message 來獲取可能的錯誤信息

也能夠省略catch部分,這樣出現異常,不作任何操做

2.18. 虛擬屬性
虛擬屬性也是對象的屬性,可是虛擬的,非模型對象的真實屬性,這樣的好處是當模板須要額外的用於顯示的屬性的時候但又不想更改模型,即可以採用這種辦法 如beetl內置的虛擬屬性.~size 針對了數組以及集合類型。

${user.gender}
${user.~genderShowName}
~genderShowName 是虛擬屬性,其內部實現根據boolean變量gender來顯示性別

如何完成虛擬屬性,請參考高級用法

2.19. 函數調用
Beetl內置了少許實用函數,能夠在Beetl任何地方調用。以下例子是調用date 函數,不傳參數狀況下,返回當前日期

<%
var date = date();
var len = strutil.length("cbd");
println("len="+len);

%>
注意函數名支持namespace方式,所以代碼第3行調用的函數是strutil.length

定義beetl的方法很是容易,有三種方法

實現Function類的call方法,並添加到配置文件裏,或者顯示的經過代碼註冊registerFunction(name,yourFunction)

能夠直接調用registerFunctionPackage(namespace,yourJavaObject),這時候yourJavaObject裏的全部public方法都將註冊爲Beetl方法,方法名是namespace+"."+方法名

能夠直接寫模板文件而且以html做爲後綴,放到root/functions目錄下,這樣此模板文件自動註冊爲一個函數,其函數名是該模板文件名。

詳情請參考高級用法

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") 獲取該變量

type.new 建立一個對象實例,如 var user = type.new("com.xx.User"); 若是配置了IMPORT_PACKAGE,則能夠省略包名,type.new("User")

type.name 返回一個實例的名字,var userClassName = type.name(user),返回"User"

2.20. 安全輸出
安全輸出是任何一個模板引擎必須重視的問題,不然,將極大困擾模板開發者。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,輸出」單身」

譬如

${user.birthday!@System.constants.DefaultBir}, 表示若是user爲null,或者user. birthday爲null,輸出System.constants.DefaultBir

還有一種狀況不多發生,但也有可能,輸出模板變量發生的任何異常,如變量內部拋出的一個異常

這須要使用格式${!(變量)},這樣,在變量引用發生任何異常狀況下,都不做輸出,譬如

${!(user.name)},,beetl將會調用user.getName()方法,若是發生異常,beetl將會忽略此異常,繼續渲染

值得注意的是,在變量後加上!不只僅能夠應用於佔位符輸出(但主要是應用於佔位符輸出),也能夠用於表達式中,如:


<%

<%
var k = user.name!'N/A'+user.age!;
%>
${k}

%>
若是user爲null,則k值將爲N/A

在有些模板裏,可能整個模板都須要安全輸出,也可能模板的部分須要安全輸出,使用者沒必要爲每個表達式使用!,可使用beetl的安全指示符號來完成安全輸出 如:
<%
DIRECTIVE SAFE_OUTPUT_OPEN;
%>
${user.wife.name}
模板其餘內容,均能安全輸出……
<%
//關閉安全輸出。
DIRECTIVE SAFE_OUTPUT_CLOSE;
%>
Beetl不建議每個頁面都使用DIRECTIVE SAFE_OUTPUT_OPEN,這樣,若是若是真有不指望的錯誤,不容易及時發現,其次,安全輸出意味着beetl會有額外的代碼檢測值是否存在或者是否爲null,性能會略差點。因此建議及時關閉安全輸出(這不是必須的,但頁面全部地方是安全輸出,可能不容易發現錯誤)

在for-in 循環中 ,也能夠爲集合變量增長安全輸出指示符號,這樣,若是集合變量爲null,也能夠不進入循環體,如:
<%
var list = null;
for(item in list!){

}eslefor{
print("no data");
}
%>
2.20.1. 變量是否存在

判斷變量是否存在,能夠採用內置的has或者isEmpty方法來判斷,參數是變量,如

<%
if(has(flag)){
print("flag變量存在,能夠訪問")
}
%>
若是須要判斷變量是否存在,若是存在,還有其餘判斷條件,一般都這麼寫

<%
if(has(flag)||flag==0){
//code
}
%>
若是flag不存在,或者flag存在,但值是0,都將執行if語句

可是,有更爲簡便的方法是直接用安全輸出,如

<%
if(flag!0==0){
//code
}
%>
flag!0 取值是這樣的,若是flag不存在,則爲0,若是存在,則取值flag的值,相似三元表達式 has(flag)?falg:0

2.20.2. 安全輸出表達式

安全輸出表達式能夠包括

字符串常量,如 ${user.count!"無結果"}

boolean常量 ${user.count!false}

數字常量,僅限於正數,由於若是是負數,則相似減號,容易誤用,所以,若是須要表示負數,請用括號,如${user.count!(-1)}

class直接調用,如${user.count!@User.DEFAULT_NUM}

方法調用,如 ${user.count!getDefault() }

屬性引用,如 ${user.count!user.maxCount }

任何表達式,須要用括號

2.21. 格式化
幾乎全部的模板語言都支持格式化,Beetl也不列外,以下例子Beetl提供的內置日期格式
<% 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 設置了了格式化函數,所以上面的例子能夠簡化爲

${date,「yyyy-MM-dd」}.
Beetl針對日期和數字類型提供的默認的格式化函數,在org/beetl/core/beetl-default.properties裏,註冊了

##內置的格式化函數
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.format.NumberFormat
FTC.java.util.concurrent.atomic.AtomicInteger = org.beetl.ext.format.NumberFormat
2.22. 標籤函數
所謂標籤函數,即容許處理模板文件裏的一塊內容,功能等於同jsp tag。如Beetl內置的layout標籤

index.html
<%
layout("/inc/layout.html",{title:'主題'}){
%>
Hello,this is main part
<%} %>
layout.html

title is ${title}
body content ${layoutContent}
footer
第1行變量title來自於layout標籤函數的參數

第2行layoutContent 是layout標籤體{}渲染後的結果

關於layout標籤,參考高級主題佈局

Beetl內置了另一個標籤是include,容許 include 另一個模板文件

<%
include("/inc/header.html"){}
%>
在標籤中,{} 內容將依據標籤的實現而執行,layout標籤將執行{}中的內容,而include標籤則忽略標籤體內容。

關於如何實現標籤函數,請參考高級主題,以下是一個簡單的的標籤函數:

public class CompressTag extends Tag
{

@Override
public void render()
{
BodyContent content = getBodyContent();
String content = content.getBody();
String zip = compress(conent);
ctx.byteWriter.write(zip);

}

}
2.23. HTML標籤
Beetl 也支持HTML tag形式的標籤, 區分beetl的html tag 與 標準html tag。如設定HTML_TAG_FLAG=#,則以下html tag將被beetl解析

<#footer style=」simple」/>
<#richeditor id=」rid」 path="${ctxPath}/upload" name=」rname」 maxlength=」${maxlength}」> ${html} …其餘模板內容 </#richdeitor>
<#html:input id=’aaaa’ />
如對於標籤footer,Beetl默認會尋找WebRoot/htmltag/footer.tag(能夠經過配置文件修改路徑和後綴) ,內容以下:

<%if(style==’simple’){%>
請聯繫我 ${session.user.name}
<%}else{%>
請聯繫我 ${session.user.name},phone:${session.user.phone}
<%}%>
以下還包含了自定義html標籤一些一些規則

能夠在自定義標籤裏引用標籤體的內容,標籤體能夠是普通文本,beetl模板,以及嵌套的自定義標籤等。如上<#richeditor 標籤體裏,可用「tagBody」來引用

HTML自定義標籤 的屬性值均爲字符串 如<#input value=」123」 />,在input.tag文件裏 變量value的類型是字符串

能夠在屬性標籤裏引用beetl變量,如<#input value=」${user.age}」 />,此時在input.tag裏,value的類型取決於user.age

在屬性裏引用beetl變量,不支持格式化,如<#input value=」${user.date,‘yyyy-MM-dd’ }」 />,若是須要格式化,須要在input.tag文件裏自行格式化

在標籤屬性裏傳json變量須要謹慎,由於json包含了"}",容易與佔位符混合致使解析出錯,所以得使用"\"符號,如<#input value=」${ {age:25} }」 />

html tag 屬性名將做爲 其對應模板的變量名。

默認機制下,全局變量都將傳給html tag對應的模板文件,這個跟include同樣。固然,這機制也能夠改變,對於標籤來講,一般是做爲一個組件存在,也不必定須要徹底傳送全部全局變量,而只傳送(request,session,這樣變量),所以須要從新繼承org.beetl.ext.tag.HTMLTagSupportWrapper.並重載callHtmlTag方法。並註冊爲htmltag標籤。具體請參考https://github.com/javamonkey/beetl2.0/blob/master/beetl-core/src/test/java/org/beetl/core/tag/HtmlTagTest.java

若是採用模板來寫html標籤功能不夠強大,beetl支持寫標籤函數(參考上一節)來實現html標籤,標籤函數args[0]表示標籤名,這一般沒有什麼用處,args[1] 則是標籤的屬性,參數是個map,key是html tag的屬性,value是其屬性值,以下用java完成的html 標籤用於輸出屬性值

public class SimpleHtmlTag extends Tag
{

@Override
public void render()
{
String tagName = (String) this.args[0];
Map attrs = (Map) args[1];
String value = (String) attrs.get("attr");
try
{
this.ctx.byteWriter.writeString(value);
}
catch (IOException e)
{

}

}

}
若是註冊gt.registerTag("simpleTag", SimpleHtmlTag.class); 則以下模板輸出了attr屬性值abc

<#simpleTag attr="abc"></#simpleTag>
HTML_TAG_FLAG默認爲#用來區別是不是beetl的html tag,你也能夠設置成其餘符號,好比 "my:",這樣,<my:table></my:table> 實際上是一個指向table.tag的標籤實現

2.24. 綁定變量的HTML標籤
對於html標籤(參考上一節),Beetl還 支持將標籤實現類(java代碼)裏的對象做爲臨時變量,被標籤體引用。此時須要實現GeneralVarTagBinding (此類是Tag的子類) 該類提供另外3個個方法 - void binds(Object… array) 子類在render方法裏調用此類以實現變量綁定,綁定順序同在模板中申明的順序 - void bind(String name, Object value) 子類在render方法裏調用此類以實現變量綁定,name是模板中申明的變量名,用此方法綁定不如binds更靈活,再也不推薦 - Object getAttributeValue 得到標籤的屬性 - Map getAttributes 得到標籤的全部屬性

public class TagSample extends GeneralVarTagBinding
{

@Override
public void render()
{
int limit = Integer.parseInt((String) this.getAttributeValue("limit"));
for (int i = 0; i < limit; i++)
{
this.binds(i)
this.doBodyRender();
}

}

}


//在某處註冊一下標籤TagSample
//gt.registerTag("tag", TagSample.class);
如上例子,render方法將循環渲染標籤體limit次,且每次都將value賦值爲i。咱們再看看模板如何寫的

<#tag limit="3" ; value>
${value}
</#tag>
相似於常規html標籤,須要在標籤的最後的屬性定義後面加上分號 ";" 此分號表示這個是一個須要在標籤運行時須要綁定變量的標籤。後跟上要綁定的變量列表,如上例只綁定了一個value變量,若是須要綁定多個變量,則用逗號分開,如var1,var2 上。若是後面沒有變量列表,只有分號,則默認綁定到標籤名同名的變量上. 若是標籤有namesapce,則默認綁定訂的變量名不包含namespace

注意,因爲標籤使用由於太長可能換行或者是文本格式化致使換行,目前beetl只容許在屬性之間換行,不然,將報標籤解析錯誤。

默認狀況下,若是標籤屬性出現了var(能夠經過配置文件改爲其餘屬性名),也認爲是綁定變量的標籤,如上面的例子也能夠這麼寫

<#tag limit="3" var="value">
${value}
</#tag>
var屬性的值能夠是個以逗號分開的變量名列表,如var="total,customer,index"

2.25. 直接調用java方法和屬性
能夠經過符號@來代表後面表達式調用是java風格,能夠調用對象的方法,屬性

${@user.getMaxFriend(「lucy」)}
${@user.maxFriend[0].getName()}
${@com.xxxx.constants.Order.getMaxNum()}
${@com.xxxx.User$Gender.MAN}
<%
var max = @com.xxxx.constants.Order.MAX_NUM;
var c =1;
var d = @user.getAge(c);
%>
能夠調用instance的public方法和屬性,也能夠調用靜態類的屬性和方法 ,須要加一個 @指示此調用是直接調用class,其後的表達式是java風格的。

GroupTemplate能夠配置爲不容許直接調用Class,具體請參考配置文件.

也能夠經過安全管理器配置到底哪些類Beetl不容許調用,具體請參考高級用法。默認狀況,java.lang.Runtime,和 java.lang.Process不容許在模板裏調用。你本身的安全管理器也許能夠配置爲不能直接訪問DAO類(避免了之前jsp能夠訪問任意代碼帶來的危害)

請按照java規範寫類名和方法名,屬性名。這樣便於beetl識別到底調用的是哪一個類,哪一個方法。不然會拋出錯誤

能夠省略包名,只用類名。beetl將搜索包路徑找到合適的類(須要設置配置「IMPORT_PACKAGE=包名.;包名.」,包名後須要跟一個「.」, 或者調用Configuration.addPkg)方法具體請參考附件配置文件說明

內部類(包括枚舉)訪問同java同樣,如User類有個內部枚舉類Gender,訪問是User$Gender

表達式是java風格,但參數仍然是beetl表達式,好比 @user.sayHello(user.name).這裏user.sayHello是java調用,user.name 仍然是beetl表達式

2.26. 嚴格MVC控制
若是在配置文件中設置了嚴格MVC,則如下語法將不在模板文件裏容許,不然將報出STRICK_MVC 錯誤

定義變量,爲變量賦值,如var a = 12是非法的

算術表達式 如${user.age+12}是非法的

除了只容許布爾之外,不容許邏輯表達式和方法調用 如if(user.gender==1)是非法的

方法調用,如${subString(string,1)}是非法的

Class方法和屬性調用,如${@user.getName()}是非法的

嚴格的MVC,很是有助於邏輯與視圖的分離,特別當邏輯與視圖是由倆個團隊來完成的。若是你嗜好嚴格MVC,能夠調用groupTemplate.enableStrict()

經過重載AntlrProgramBuilder,能夠按照本身的方法控制到底哪些語法是不容許在模板引擎中出現的,但這已經超出了Beetl模板的基礎使用

2.27. 指令
指令格式爲: DIRECTIVE 指令名 指令參數(可選) Beetl目前支持安全輸出指令,分別是

DIRECTIVE SAFE_OUTPUT_OPEN ; 打開安全輸出功能,此指令後的全部表達式都具備安全輸出功能,

DIRECTIVE SAFE_OUTPUT_CLOSE ; 關閉安全輸出功能。詳情參考安全輸出

DIRECTIVE DYNAMIC varName1,varName2 …指示後面的變量是動態類型,Beetl應該考慮爲Object. 也能夠省略後面的變量名,則表示模板裏全部變量都是Object

1
2
<% DIRECTIVE DYNAMIC idList;
for(value in idList) .....
DYNAMIC 一般用在組件模板裏,由於組件模板能夠接收任何類型的對象。如列表控件,能夠接收任何含有id和 value屬性的對象。

1 注意 DYNAMIC 後的變量名也容許用引號,這主要是兼容Beetl1.x版本

2 Beetl1.x 指令都是大寫,當前版本也容許小寫,如 directive dynamic idList

2.28. 類型聲明
Beetl 本質上仍是強類型的模板引擎,即模板每一個變量類型是特定的,在模板運行過程當中,beetl 會根據全局變量自動推測出模板中各類變量和表達式類型。 也能夠經過類型申明來講明beetl全局變量的類型,以下格式

<%
/**
*@type (List<User> idList,User user)
*/
for(value in idList) .....
類型申明必須放到多行註釋裏,格式是@type( … ),裏面的申明相似java方法的參數申明。正如你看到的類型申明是在註釋裏,也就代表了這在Beetl模板引擎中不是必須的,或者你只須要申明一部分便可,之因此提供可選的類型說明,是由於

提升一點性能

最重要的是,提升了模板的可維護性。可讓模板維護者知道變量類型,也可讓將來的ide插件根據類型聲明來提供屬性提示,重構等高級功能

須要注意的是,若是在類型聲明裏提供的是類名,而不是類全路徑,這樣必須在配置文件裏申明類的搜索路徑((須要設置配置IMPORT_PACKAGE=包名.;包名.,或者調用Configuration.addPkg)),默認的搜索路徑有java.util. 和 java.lang.

2.29. 錯誤處理
Beetl能較爲詳細的顯示錯誤緣由,包括錯誤行數,錯誤符號,錯誤內容附近的模板內容,以及錯誤緣由,若是有異常,還包括異常和異常信息。 默認狀況下,僅僅在控制檯顯示,以下代碼:

<%
var a = 1;
var b = a/0;
%>
運行此模板後,錯誤提示以下:

>>DIV_ZERO_ERROR:0 位於3行 資源:/org/beetl/sample/s0125/error1.txt
1|<%
2|var a = 1;
3|var b = a/0;
4|%>

<%
var a = 1;
var b = a
var c = a+2;
%>
運行此模板後

>>缺乏符號(PARSER_MISS_ERROR):缺乏輸入 ';' 在 'var' 位於4行 資源:/org/beetl/sample/s0125/error2.txt
1|<%
2|var a = 1;
3|var b = a
4|var c = a+2;
5|%>
1 默認的錯誤處理器僅僅像後臺打印錯誤,並無拋出異常,若是須要在render錯誤時候拋出異常到控制層,則可使用org.beetl.core.ReThrowConsoleErrorHandler。不只打印異常,還拋出BeetlException,

2 能夠自定義異常處理器,好比把錯誤輸出到 做爲渲染結果一部分輸出,或者輸出更美觀的html內容等,具體參考高級用法

3 能夠在配置文件不設置異常,這樣Beetl引擎將不處理異常,用戶能夠在外部來處理(能夠在外部調用ErrorHandler子類來顯示異常)

2.30. Beetl小工具
BeetlKit 提供了一些便利的方法讓你馬上能使用Beetl模板引擎。提供了以下方法

public static String render(String template, Map<String, Object> paras) 渲染模板,使用paras參數,渲染結果做爲字符串返回

public static void renderTo(String template, Writer writer, Map<String, Object> paras) 渲染模板,使用paras參數,渲染結果做爲字符串返回

public static void execute(String script, Map<String, Object> paras) 執行某個腳本

public static Map execute(String script, Map<String, Object> paras, String[] locals) 執行某個腳本,將locals指定的變量名和模板執行後相應值放入到返回的Map裏

public static Map executeAndReturnRootScopeVars(String script) 執行某個腳本,返回全部頂級scope的全部變量和值

public static String testTemplate(String template, String initValue) 渲染模板template,其變量來源於intValue腳本運行的結果,其全部頂級Scope的變量都將做爲template的變量

public static String testTemplate(String template, String initValue) 渲染模板template,其變量來源於intValue腳本運行的結果,其全部頂級Scope的變量都將做爲template的變量

String template = "var a=1,c=2+1;";
Map result = executeAndReturnRootScopeVars(template);
System.out.println(result);
//輸出結果是{c=3, a=1}
BeetlKit 不要用於線上系統。僅僅做爲體驗Beetl功能而提供的,若是須要在線上使用這些功能,請參考該類源碼自行擴展

2.31. 瑣碎功能
對齊:我發現別的模板語言要是作到對齊,很是困難,使用Beetl你徹底不用擔憂,好比velocty,stringtemlate,freemarker例子都出現了不對齊的狀況,影響了美觀,Beetl徹底無需擔憂輸出對齊

Escape:可使用\ 作escape 符號,如\$monkey\$ 將做爲一個普通的文本,輸出爲$monkey$.再如爲了在後加上美圓符號(佔位符剛好又是美圓符號)能夠用這倆種方式hello,it’s $money$\$, 或者Hello,it’s $money+"\$"$ 。若是要輸出\符號本生,則須要用倆個\\,這點與javascript,java 語義一致.
相關文章
相關標籤/搜索