1. 程式人生 > >freemaker介紹及常見用法

freemaker介紹及常見用法

總結一下freemaker的相關知識

freemaker(FTL)是一個模板引擎,即一種基於模板和要改變的資料, 並用來生成輸出文字(HTML網頁、電子郵件配置檔案原始碼等)的通用工具。 它不是面向終端使用者的,而是一個Java類庫,是一款程式設計師可以嵌入他們所開發產品的元件

在javaweb中,經常會使用freemaker模板來生成網頁,它的基本工作機制如下:


下面介紹一下它的常見用法

一、FTL指令規則

在FreeMarker中,使用FTL標籤來使用指令,FreeMarker有3種FTL標籤,這和HTML標籤是完全類似的. 
1、開始標籤:<#標籤 引數> 
2、結束標籤:</#標籤 > 
3、空標籤:<#標籤 引數/> 

實際上,使用標籤時前面的符號#也可能變成@,如果該指令是一個使用者指令而不是系統內建指令時,應將#符號改成@符號. 

使用FTL標籤時, 應該有正確的巢狀,而不是交叉使用,這和XML標籤的用法完全一樣.如果全用不存在的指令,FreeMarker不會使用模板輸出,而是產生一個錯誤訊息.FreeMarker會忽略FTL標籤中的空白字元.值得注意的是< , /> 和指令之間不允許有空白字元. 

二、插值規則 

FreeMarker的插值有如下兩種型別:1,通用插值${expr};2,數字格式化插值:#{expr}或#{expr;format} 

1、通用插值

對於通用插值,又可以分為以下4種情況: 
a)插值結果為字串值:直接輸出表達式結果 
b)插值結果為數字值:根據預設格式(由#setting指令設定)將表示式結果轉換成文字輸出.可以使用內建的字串函式格式化單個插值,如下面的例子: 
<#settion number_format="currency"/> 
<#assign answer=42/> 
${answer} 
${answer?string} <#-- the same as ${answer} --> 
${answer?string.number} 
${answer?string.currency} 
${answer?string.percent} 
${answer} 
輸出結果是: 
$42.00 
$42.00 
42 
$42.00 
4,200% 
c)插值結果為日期值:根據預設格式(由#setting指令設定)將表示式結果轉換成文字輸出.可以使用內建的字串函式格式化單個插值,如下面的例子: 
${lastUpdated?string("yyyy-MM-dd HH:mm:ss zzzz")} 
${lastUpdated?string("EEE, MMM d, ''yy")} 
${lastUpdated?string("EEEE, MMMM dd, yyyy, hh:mm:ss a '('zzz')'")} 
輸出結果是: 
2008-04-08 08:08:08 Pacific Daylight Time 
Tue, Apr 8, '03 
Tuesday, April 08, 2003, 08:08:08 PM (PDT) 
d)插值結果為布林值:根據預設格式(由#setting指令設定)將表示式結果轉換成文字輸出.可以使用內建的字串函式格式化單個插值,如下面的例子: 
<#assign foo=true/> 
${foo?string("yes", "no")} 
輸出結果是: 
yes 

2、數字格式化插值

數字格式化插值可採用#{expr;format}形式來格式化數字,其中format可以是: 
mX:小數部分最小X位 
MX:小數部分最大X位 
如下面的例子: 
<#assign x=2.582/> 
<#assign y=4/> 
#{x; M2} <#-- 輸出2.58 --> 
#{y; M2} <#-- 輸出4 --> 
#{x; m2} <#-- 輸出2.6 --> 
#{y; m2} <#-- 輸出4.0 --> 
#{x; m1M2} <#-- 輸出2.58 --> 
#{x; m1M2} <#-- 輸出4.0 -->

三、表示式

表示式是FreeMarker模板的核心功能,表示式放置在插值語法${}之中時,表明需要輸出表達式的值;表示式語法也可與FreeMarker 標籤結合,用於控制輸出.實際上FreeMarker的表示式功能非常強大,它不僅支援直接指定值,輸出變數值,也支援字串格式化輸出和集合訪問等功能. 

1 、直接指定值 

使用直接指定值語法讓FreeMarker直接輸出插值中的值,而不是輸出變數值.直接指定值可以是字串,數值,布林值,集合和MAP物件. 
1.1字串 
直接指定字串值使用單引號或雙引號限定,如果字串值中包含特殊字元需要轉義,看下面的例子: 
${"我的檔案儲存在C:\\盤"} 
${'我名字是\"annlee\"'} 
輸出結果是: 
我的檔案儲存在C:\盤 
我名字是"annlee" 

FreeMarker支援如下轉義字元: 
\";雙引號(u0022) 
\';單引號(u0027) 
\\;反斜槓(u005C) 
\n;換行(u000A) 
\r;回車(u000D) 
\t;Tab(u0009) 
\b;退格鍵(u0008) 
\f;Form feed(u000C) 
\l;< 
\g;> 
\a;& 
\{;{ 
\xCode;直接通過4位的16進位制數來指定Unicode碼,輸出該unicode碼對應的字元. 

如果某段文字中包含大量的特殊符號,FreeMarker提供了另一種特殊格式:可以在指定字串內容的引號前增加r標記,在r標記後的檔案將會直接輸出.看如下程式碼: 
${r"${foo}"} 
${r"C:\foo\bar"} 
輸出結果是: 
${foo} 
C:\foo\bar 
1.2數值 
表示式中的數值直接輸出,不需要引號.小數點使用"."分隔,不能使用分組","符號.FreeMarker目前還不支援科學計數法,所以"1E3"是錯誤的.在FreeMarker表示式中使用數值需要注意以下幾點: 
1,數值不能省略小數點前面的0,所以".5"是錯誤的寫法 
2,數值8 , +8 , 8.00都是相同的 
3,布林值 直接使用true和false,不使用引號. 
4,集合 
集合以方括號包括,各集合元素之間以英文逗號","分隔,看如下的例子: 
<#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as x> 
${x} 
</#list> 
輸出結果是: 
星期一 
星期二 
星期三 
星期四 
星期五 
星期六 
星期天 

除此之外,集合元素也可以是表示式,例子如下: 
[2 + 2, [1, 2, 3, 4], "whatnot"] 
還可以使用數字範圍定義數字集合,如2..5等同於[2, 3, 4, 5],但是更有效率.注意,使用數字範圍來定義集合時無需使用方括號,數字範圍也支援反遞增的數字範圍,如5..2 

5,Map物件 
Map物件使用花括號包括,Map中的key-value對之間以英文冒號":"分隔,多組key-value對之間以英文逗號","分隔.下面是一個例子: 
{"語文":78, "數學":80} 
Map物件的key和value都是表示式,但是key必須是字串 

2、輸出變數值 

FreeMarker的表示式輸出變數時,這些變數可以是頂層變數,也可以是Map物件中的變數,還可以是集合中的變數,並可以使用點(.)語法來訪問Java物件的屬性.下面分別討論這些情況 
2.1頂層變數 
所謂頂層變數就是直接放在資料模型中的值,例如有如下資料模型: 
Map root = new HashMap();   //建立資料模型 
root.put("name","annlee");   //name是一個頂層變數 

對於頂層變數,直接使用${variableName}來輸出變數值,變數名只能是字母,數字,下劃線,$,@和#的組合,且不能以數字開頭號.為了輸出上面的name的值,可以使用如下語法: 
${name} 
2.2輸出集合元素 
如果需要輸出集合元素,則可以根據集合元素的索引來輸出集合元素,集合元素的索引以方括號指定.假設有索引: 
["星期一","星期二","星期三","星期四","星期五","星期六","星期天"].該索引名為week,如果需要輸出星期三,則可以使用如下語法: 
${week[2]}   //輸出第三個集合元素 

此外,FreeMarker還支援返回集合的子集合,如果需要返回集合的子集合,則可以使用如下語法: 
week[3..5]   //返回week集合的子集合,子集合中的元素是week集合中的第4-6個元素 
2.3輸出Map元素 
這裡的Map物件可以是直接HashMap的例項,甚至包括JavaBean例項,對於JavaBean例項而言,我們一樣可以把其當成屬性為key,屬性值為value的Map例項.為了輸出Map元素的值,可以使用點語法或方括號語法.假如有下面的資料模型: 
Map root = new HashMap(); 
Book book = new Book(); 
Author author = new Author(); 
author.setName("annlee"); 
author.setAddress("gz"); 
book.setName("struts2"); 
book.setAuthor(author); 
root.put("info","struts"); 
root.put("book", book); 

為了訪問資料模型中名為struts2的書的作者的名字,可以使用如下語法: 
book.author.name    //全部使用點語法 
book["author"].name 
book.author["name"]    //混合使用點語法和方括號語法 
book["author"]["name"]   //全部使用方括號語法 

使用點語法時,變數名字有頂層變數一樣的限制,但方括號語法沒有該限制,因為名字可以是任意表達式的結果. 

3、字串操作 

FreeMarker的表示式對字串操作非常靈活,可以將字串常量和變數連線起來,也可以返回字串的子串等. 
字串連線有兩種語法: 
1,使用${..}或#{..}在字串常量部分插入表示式的值,從而完成字串連線. 
2,直接使用連線運算子+來連線字串 

例如有如下資料模型: 
Map root = new HashMap(); root.put("user","annlee"); 
下面將user變數和常量連線起來: 
${"hello, ${user}!"}   //使用第一種語法來連線 
${"hello, " + user + "!"} //使用+號來連線 
上面的輸出字串都是hello,annlee!,可以看出這兩種語法的效果完全一樣. 

值得注意的是,${..}只能用於文字部分,不能用於表示式,下面的程式碼是錯誤的: 
<#if ${isBig}>Wow!</#if> 
<#if "${isBig}">Wow!</#if> 
應該寫成:<#if isBig>Wow!</#if> 

擷取子串可以根據字串的索引來進行,擷取子串時如果只指定了一個索引值,則用於取得字串中指定索引所對應的字元;如果指定兩個索引值,則返回兩個索引中間的字串子串.假如有如下資料模型: 
Map root = new HashMap(); root.put("book","struts2,freemarker"); 
可以通過如下語法來擷取子串: 
${book[0]}${book[4]}   //結果是su 
${book[1..4]}     //結果是tru 

4、集合連線運算子 

這裡所說的集合運算子是將兩個集合連線成一個新的集合,連線集合的運算子是+,看如下的例子: 
<#list ["星期一","星期二","星期三"] + ["星期四","星期五","星期六","星期天"] as x> 
${x} 
</#list> 
輸出結果是:星期一 星期二 星期三 星期四 星期五 星期六 星期天 

5、Map連線運算子 

Map物件的連線運算子也是將兩個Map物件連線成一個新的Map物件,Map物件的連線運算子是+,如果兩個Map物件具有相同的key,則右邊的值替代左邊的值.看如下的例子: 
<#assign scores = {"語文":86,"數學":78} + {"數學":87,"Java":93}> 
語文成績是${scores.語文} 
數學成績是${scores.數學} 
Java成績是${scores.Java} 
輸出結果是: 
語文成績是86 
數學成績是87 
Java成績是93 

6、算術運算子 

FreeMarker表示式中完全支援算術運算,FreeMarker支援的算術運算子包括:+, - , * , / , % 看如下的程式碼: 
<#assign x=5> 
${ x * x - 100 } 
${ x /2 } 
${ 12 %10 } 
輸出結果是: 
-75   2.5   2 

在表示式中使用算術運算子時要注意以下幾點: 
1,運算子兩邊的運算數字必須是數字 
2,使用+運算子時,如果一邊是數字,一邊是字串,就會自動將數字轉換為字串再連線,如:${3 + "5"},結果是:35 

使用內建的int函式可對數值取整,如: 
<#assign x=5> 
${ (x/2)?int } 
${ 1.1?int } 
${ 1.999?int } 
${ -1.1?int } 
${ -1.999?int } 
結果是:2 1 1 -1 -1 

7、比較運算子 

表示式中支援的比較運算子有如下幾個: 
1,=或者==:判斷兩個值是否相等. 
2,!=:判斷兩個值是否不等. 
3,>或者gt:判斷左邊值是否大於右邊值 
4,>=或者gte:判斷左邊值是否大於等於右邊值 
5,<或者lt:判斷左邊值是否小於右邊值 
6,<=或者lte:判斷左邊值是否小於等於右邊值 

注意:=和!=可以用於字串,數值和日期來比較是否相等,但=和!=兩邊必須是相同型別的值,否則會產生錯誤,而且FreeMarker是精確比較,"x","x ","X"是不等的.其它的執行符可以作用於數字和日期,但不能作用於字串,大部分的時候,使用gt等字母運算子代替>會有更好的效果,因為 FreeMarker會把>解釋成FTL標籤的結束字元,當然,也可以使用括號來避免這種情況,如:<#if (x>y)> 

8、邏輯運算子 

邏輯運算子有如下幾個: 
邏輯與:&& 
邏輯或:|| 
邏輯非:! 
邏輯運算子只能作用於布林值,否則將產生錯誤 

9、內建函式 

FreeMarker還提供了一些內建函式來轉換輸出,可以在任何變數後緊跟?,?後緊跟內建函式,就可以通過內建函式來輪換輸出變數.下面是常用的內建的字串函式: 
html:對字串進行HTML編碼 
cap_first:使字串第一個字母大寫 
lower_case:將字串轉換成小寫 
upper_case:將字串轉換成大寫 
trim:去掉字串前後的空白字元 

下面是集合的常用內建函式 
size:獲取序列中元素的個數 

下面是數字值的常用內建函式 
int:取得數字的整數部分,結果帶符號 

例如: 
<#assign test="Tom & Jerry"> 
${test?html} 
${test?upper_case?html} 
結果是:Tom &amp; Jerry   TOM &amp; JERRY 

10、空值處理運算子 

FreeMarker對空值的處理非常嚴格,FreeMarker的變數必須有值,沒有被賦值的變數就會丟擲異常,因為FreeMarker未賦值的變數強制出錯可以杜絕很多潛在的錯誤,如缺失潛在的變數命名,或者其他變數錯誤.這裡所說的空值,實際上也包括那些並不存在的變數,對於一個Java的 null值而言,我們認為這個變數是存在的,只是它的值為null,但對於FreeMarker模板而言,它無法理解null值,null值和不存在的變數完全相同. 

為了處理缺失變數,FreeMarker提供了兩個運算子: 
!:指定缺失變數的預設值 
??:判斷某個變數是否存在 

其中,!運算子的用法有如下兩種: 
variable!或variable!defaultValue,第一種用法不給缺失的變數指定預設值,表明預設值是空字串,長度為0的集合,或者長度為0的Map物件. 

使用!指定預設值時,並不要求預設值的型別和變數型別相同.使用??運算子非常簡單,它總是返回一個布林值,用法為:variable??,如果該變數存在,返回true,否則返回false 

11、運算子的優先順序 

FreeMarker中的運算子優先順序如下(由高到低排列): 
1,一元運算子:! 
2,內建函式:? 
3,乘除法:*, / , % 
4,加減法:- , + 
5,比較:> , < , >= , <= (lt , lte , gt , gte) 
6,相等:== , = , != 
7,邏輯與:&& 
8,邏輯或:|| 
9,數字範圍:.. 

實際上,我們在開發過程中應該使用括號來嚴格區分,這樣的可讀性好,出錯少

四、FreeMarker的常用指令 

FreeMarker的FTL指令也是模板的重要組成部分,這些指令可實現對資料模型所包含資料的撫今迭代,分支控制.除此之外,還有一些重要的功能,也是通過FTL指令來實現的. 

1、if指令 

這是一個典型的分支控制指令,該指令的作用完全類似於Java語言中的if,if指令的語法格式如下: 
<#if condition>... 
<#elseif condition>... 
<#elseif condition>... 
<#else> ... 
</#if> 

例子如下: 
<#assign age=23> 
<#if (age>60)>老年人 
<#elseif (age>40)>中年人 
<#elseif (age>20)>青年人 
<#else> 少年人 
</#if> 
輸出結果是:青年人 
上面的程式碼中的邏輯表示式用括號括起來主要是因為裡面有>符號,由於FreeMarker會將>符號當成標籤的結束字元,可能導致程式出錯,為了避免這種情況,我們應該在凡是出現這些符號的地方都使用括號. 

2、switch , case , default , break指令 

這些指令顯然是分支指令,作用類似於Java的switch語句,switch指令的語法結構如下: 
<#switch value> 
<#case refValue>...<#break> 
<#case refValue>...<#break> 
<#default>... 
</#switch> 

3、list, break指令 

list指令是一個迭代輸出指令,用於迭代輸出資料模型中的集合,list指令的語法格式如下: 
<#list sequence as item> 
... 
</#list> 
上面的語法格式中,sequence就是一個集合物件,也可以是一個表示式,但該表示式將返回一個集合物件,而item是一個任意的名字,就是被迭代輸出的集合元素.此外,迭代集合物件時,還包含兩個特殊的迴圈變數: 
item_index:當前變數的索引值 
item_has_next:是否存在下一個物件 
也可以使用<#break>指令跳出迭代 

例子如下: 
<#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as x> 
${x_index + 1}.${x}<#if x_has_next>,</if> 
<#if x="星期四"><#break></#if> 
</#list> 

4、include指令 

include指令的作用類似於JSP的包含指令,用於包含指定頁.include指令的語法格式如下: 
<#include filename [options]> 
在上面的語法格式中,兩個引數的解釋如下: 
filename:該引數指定被包含的模板檔案 
options:該引數可以省略,指定包含時的選項,包含encoding和parse兩個選項,其中encoding指定包含頁面時所用的解碼集,而parse指定被包含檔案是否作為FTL檔案來解析,如果省略了parse選項值,則該選項預設是true. 

5、import指令 

該指令用於匯入FreeMarker模板中的所有變數,並將該變數放置在指定的Map物件中,import指令的語法格式如下: 
<#import "/lib/common.ftl" as com> 
上面的程式碼將匯入/lib/common.ftl模板檔案中的所有變數,交將這些變數放置在一個名為com的Map物件中. 

6、noparse指令 

noparse指令指定FreeMarker不處理該指定裡包含的內容,該指令的語法格式如下: 
<#noparse>...</#noparse> 

看如下的例子: 
<#noparse> 
<#list books as book> 
   <tr><td>${book.name}<td>作者:${book.author} 
</#list> 
</#noparse> 
輸出如下: 
<#list books as book> 
   <tr><td>${book.name}<td>作者:${book.author} 
</#list> 

7、escape , noescape指令 

escape指令導致body區的插值都會被自動加上escape表示式,但不會影響字串內的插值,只會影響到body內出現的插值,使用escape指令的語法格式如下: 
<#escape identifier as expression>... 
<#noescape>...</#noescape> 
</#escape> 

看如下的程式碼: 
<#escape x as x?html> 
First name:${firstName} 
Last name:${lastName} 
Maiden name:${maidenName} 
</#escape> 
上面的程式碼等同於: 
First name:${firstName?html} 
Last name:${lastName?html} 
Maiden name:${maidenName?html} 

escape指令在解析模板時起作用而不是在執行時起作用,除此之外,escape指令也巢狀使用,子escape繼承父escape的規則,如下例子: