Beetl2.7.16中文文檔
Beetl作者:李家智 <xiandafu@126.com>
1. 什么是Beetl
Beetl目前版本是2.7.16,相對于其他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也支持自定義占位符和控制語句起始符號也支持使用者完全可以打造適合自己的工具包。
關于性能
通過與主流模板引擎Freemarker,Vecloity以及JSP對比,Beetl6倍于Freemarker,2倍于JSP。這是因為宏觀上,通過了優化的渲染引擎,IO的二進制輸出,字節碼屬性訪問增強,微觀上,通過一維數組保存上下文Context,靜態文本合并處理,通過重復使用字節數組來防止java頻繁的創建和銷毀數組,還使用模板緩存,運行時優化等方法。詳情參考附錄
獨特功能
Beetl有些功能是發展了10多年的模板引擎所不具備的,這些功能非常利于模板的開發和維護,如下
- 自定義占位符和控制語句起始符號,這有利于減小模板語法對模板的傾入性,比如在html模板中,如果定義控制語句符號是``,那么,大部分模板文件都能通過瀏覽器打開。有的使用者僅僅采用了單個符號
@
(或者單個符號“~
”)以及回車換號作為控制語句起始符號,這又能提高開發效率- 可單獨測試的模板。無需真正的控制層和模型層,Beetl的模板就可以單獨開發和測試
- 同時支持較為松散的MVC和嚴格的MVC,如果在模板語言里嵌入計算表達式,復雜條件表達式,以及函數調用有干涉業務邏輯嫌疑,你可以禁止使用這些語法。
- 強大的安全輸出,通過安全輸出符號!,能在模板變量,變量屬性引用,for循環,占位符輸出,try-catch中等各個地方提供安全輸出,保證渲染正常。
- 模板變量:運行將模板的某一部分輸出像js那樣賦值給一個變量,稍后再處理。利用模板變量能完成非常復雜的頁面布局(簡單的布局可使用include,layout標簽函數)
- 類型推測,能在運行的時候推測模板變量類型,從而優化性能,也可以通過注解的方法顯示的說明模板變量屬性(這是非必須的,但有助于IDE自動提示功能)
- 可插拔的設計,錯誤信息提示,模板引擎緩存機制,模板資源管理,本地調用的安全管理器,嚴格MVC限制,模板引擎本身都有默認的實現,但又完全可以自定義以適合特定需求
- 增強的語法,如for-elsefor, select-case,安全輸出符號!,省略的三元表達式 等,這些語法特別適合模板開發
- 局部渲染技術,結合現在js的ajax技術。
- 性能超高,具有最快的模板解釋引擎,同時,又有較低的CPU消耗。5-6倍于國內使用的Freemaker。適合各類模板應用,如代碼生成工具,CMS系統,普通網站,超高訪問量的門戶系統,和富客戶端JS框架整合的后臺管理應用
小白如何開始
- 需要通讀基本用法,大部分都是講解語法,而語法跟js很接近,所以可以快速預覽,但Beetl是針對模板設計, 所以像安全輸出,標簽和html標簽,全局變量,臨時變量和共享變量,布局技術,以及直接調用java代碼等還需要認真讀一遍。
- 如果從事web開發,還需要閱讀web集成里的第一節“web提供的全局變量”,如果web里還使用ajax技術,可以閱讀“整合ajax的局部渲染技術”。
- 包含有spring,jfinal,jodd,struts 等demo可以作為參考學習用https://git.oschina.net/xiandafu 任何問題,都可以在ibeetl.com 社區上提問。目前答復率是100%,提問需要詳細說明自己的期望,出錯信息,附上代碼或者圖片
聯系作者
作者:閑.大賦 (李家智)等(參考附錄查看代碼貢獻者)
QQ技術交流群:219324263
Beetl社區:bbs.ibeetl.com
源碼主頁:https://github.com/javamonkey/beetl2.0
在線體驗和代碼分享 http://ibeetl.com/beetlonline/
2. 基本用法
2.1. 安裝
如果使用maven,請使用如下坐標
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>2.7.16</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= /
#是否檢測文件變化,開發用true合適,但線上要改為false
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行指定了默認使用的模板資源加載器,注意,在beetl與其他MVC框架集成的時候,模板加載器不一定根據這個配置,比如spring,他的RESOURCE_LOADER以spring的配置為準
第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的支持方式一樣,如下
- Beetl支持通過”.”號來訪問對象的的屬性,如果javascript一樣。如果User對象有個getName()方法,那么在模板中,可以通過${xxx.name}來訪問
- 如果模板變量是數組或者List類,這可以通過[] 來訪問,如${userList[0]}
- 如果模板變量是Map類,這可以通過[]來訪問,如${map[“name”]},如果key值是字符串類型,也可以使用${map.name}.但不建議這么使用,因為會讓模板閱讀者誤以為是一個Pojo對象
- Beetl也支持Generic Get方式,即如果對象有一個public Object get(String key)方法,可以通過”.”號或者[]來訪問,譬如 ${activityRecord.name}或者${activityRecord[“name”] }都將調用activityRecord的 get(String key)方法。如果對象既有具體屬性,又有Generic get(這種模型設計方式是不值得鼓勵),則以具體屬性優先級高.
- Beetl也可以通過[]來引用屬性,如${user[“name”]} 相當于${user.name}.這跟javascript保持一致。但建議不這么做,因為容易讓閱讀模板的人誤認為這是一個Map類型
- Beetl 還可以定義額外的對象屬性,而無需更改java對象,這叫著虛擬屬性,如,對于所有集合,數組,都有共同的虛擬屬性size.虛擬屬性是“.~”+虛擬屬性名
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"
- global 返回一個全局變量值,參數是一個字符串,如 var user = global("user_"+i);
- cookie 返回指定的cookie對象 ,如var userCook = cookie("user"),allCookies = cookie();
2.20. 安全輸出
安全輸出是任何一個模板引擎必須重視的問題,否則,將極大困擾模板開發者。Beetl中,如果要輸出的模板變量為null,則beetl將不做輸出,這點不同于JSP,JSP輸出null,也不同于Freemarker,如果沒有用!,它會報錯.
模板中還有倆種情況會導致模板輸出異常
- 有時候模板變量并不存在(譬如子模板里)
- 模板變量為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!){
}elsefor{
print("no data");
}
%>
2.20.1. 變量是否存在
<%
if(has(flag)){
print("flag變量存在,可以訪問")
}
%>
如果需要判斷變量是否存在,如果存在,還有其他判斷條件,通常都這么寫
<%
if(has(flag)&&flag==0){
//code
}
%>
如果flag存在,而且值是0,都將執行if語句
但是,有更為簡便的方法是直接用安全輸出,如
<%
if(flag!0==0){
//code
}
%>
flag!0 取值是這樣的,如果flag不存在,則為0,如果存在,則取值flag的值,類似三元表達式 if((has(flag)?flag:0)==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
<% DIRECTIVE DYNAMIC idList;
for(value in idList) .....
DYNAMIC 通常用在組件模板里,因為組件模板可以接收任何類型的對象。如列表控件,可以接收任何含有id和 value屬性的對象。
- 注意 DYNAMIC 后的變量名也允許用引號,這主要是兼容Beetl1.x版本
- 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.
類型聲明本用于eclipse插件用來提示,但eclipse插件暫時沒有時間去做,所以類型申明現在不推薦使用
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| %>
- 默認的錯誤處理器僅僅像后臺打印錯誤,并沒有拋出異常,如果需要在render錯誤時候拋出異常到控制層,則可以使用org.beetl.core.ReThrowConsoleErrorHandler。不僅打印異常,還拋出BeetlException
- 可以自定義異常處理器,比如把錯誤輸出到 作為渲染結果一部分輸出,或者輸出更美觀的html內容等,具體參考高級用法
- 可以在配置文件不設置異常,這樣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的變量
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 語義一致.