一篇很全面的freemarker教程

weixin_34162629發表於2015-08-25

FreeMarker的模板檔案並不比HTML頁面複雜多少,FreeMarker模板檔案主要由如下4個部分組成: 
1,文字:直接輸出的部分 
2,註釋:<#-- ... -->格式部分,不會輸出 
3,插值:即${...}或#{...}格式的部分,將使用資料模型中的部分替代輸出 
4,FTL指令:FreeMarker指定,和HTML標記類似,名字前加#予以區分,不會輸出 

下面是一個FreeMarker模板的例子,包含了以上所說的4個部分 
<html><br> 
<head><br> 
<title>Welcome!</title><br> 
</head><br> 
<body><br> 
<#-- 註釋部分 --><br> 
<#-- 下面使用插值 --> 
<h1>Welcome ${user} !</h1><br> 
<p>We have these animals:<br> 
<u1><br> 
<#-- 使用FTL指令 --> 
<#list animals as being><br> 
   <li>${being.name} for ${being.price} Euros<br> 
<#list><br> 
<u1><br> 
</body><br> 
</html> 

1, FTL指令規則 

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

實際上,使用標籤時前面的符號#也可能變成@,如果該指令是一個使用者指令而不是系統內建指令時,應將#符號改成@符號. 
使用FTL標籤時, 應該有正確的巢狀,而不是交叉使用,這和XML標籤的用法完全一樣.如果全用不存在的指令,FreeMarker不會使用模板輸出,而是產生一個錯誤訊息.FreeMarker會忽略FTL標籤中的空白字元.值得注意的是< , /> 和指令之間不允許有空白字元. 

2, 插值規則 

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

2.1 通用插值 

對於通用插值,又可以分為以下4種情況: 
1,插值結果為字串值:直接輸出表示式結果 
2,插值結果為數字值:根據預設格式(由#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% 
3,插值結果為日期值:根據預設格式(由#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) 
4,插值結果為布林值:根據預設格式(由#setting指令設定)將表示式結果轉換成文字輸出.可以使用內建的字串函式格式化單個插值,如下面的例子: 
<#assign foo=true/> 
${foo?string("yes", "no")} 
輸出結果是: 
yes 

2.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 --> 

3, 表示式 

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

3.1 直接指定值 

使用直接指定值語法讓FreeMarker直接輸出插值中的值,而不是輸出變數值.直接指定值可以是字串,數值,布林值,集合和MAP物件. 

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 

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必須是字串 

3.2 輸出變數值 

FreeMarker的表示式輸出變數時,這些變數可以是頂層變數,也可以是Map物件中的變數,還可以是集合中的變數,並可以使用點(.)語法來訪問Java物件的屬性.下面分別討論這些情況 

1,頂層變數 
所謂頂層變數就是直接放在資料模型中的值,例如有如下資料模型: 
Map root = new HashMap();   //建立資料模型 
root.put("name","annlee");   //name是一個頂層變數 

對於頂層變數,直接使用${variableName}來輸出變數值,變數名只能是字母,數字,下劃線,$,@和#的組合,且不能以數字開頭號.為了輸出上面的name的值,可以使用如下語法: 
${name} 

2,輸出集合元素 
如果需要輸出集合元素,則可以根據集合元素的索引來輸出集合元素,集合元素的索引以方括號指定.假設有索引: 
["星期一","星期二","星期三","星期四","星期五","星期六","星期天"].該索引名為week,如果需要輸出星期三,則可以使用如下語法: 
${week[2]}   //輸出第三個集合元素 

此外,FreeMarker還支援返回集合的子集合,如果需要返回集合的子集合,則可以使用如下語法: 
week[3..5]   //返回week集合的子集合,子集合中的元素是week集合中的第4-6個元素 

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.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 

3.4 集合連線運算子 

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

3.5 Map連線運算子 

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

3.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 

3.7 比較運算子 

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

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

3.8 邏輯運算子 

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

3.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 

3.10 空值處理運算子 

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

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

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

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

3.11 運算子的優先順序 

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

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

4 FreeMarker的常用指令 

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

4.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會將>符號當成標籤的結束字元,可能導致程式出錯,為了避免這種情況,我們應該在凡是出現這些符號的地方都使用括號. 

4.2 switch , case , default , break指令 

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

4.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.4 include指令 

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

4.5 import指令 

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

4.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> 

4.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的規則,如下例子: 
<#escape x as x?html> 
Customer Name:${customerName} 
Items to ship; 
<#escape x as itemCodeToNameMap[x]> 
   ${itemCode1} 
   ${itemCode2} 
   ${itemCode3} 
   ${itemCode4} 
</#escape> 
</#escape> 
上面的程式碼類似於: 
Customer Name:${customerName?html} 
Items to ship; 
${itemCodeToNameMap[itemCode1]?html} 
${itemCodeToNameMap[itemCode2]?html} 
${itemCodeToNameMap[itemCode3]?html} 
${itemCodeToNameMap[itemCode4]?html} 

對於放在escape指令中所有的插值而言,這此插值將被自動加上escape表示式,如果需要指定escape指令中某些插值無需新增escape表示式,則應該使用noescape指令,放在noescape指令中的插值將不會新增escape表示式. 

4.8 assign指令 

assign指令在前面已經使用了多次,它用於為該模板頁面建立或替換一個頂層變數,assign指令的用法有多種,包含建立或替換一個頂層變數, 或者建立或替換多個變數等,它的最簡單的語法如下:<#assign name=value [in namespacehash]>,這個用法用於指定一個名為name的變數,該變數的值為value,此外,FreeMarker允許在使用 assign指令裡增加in子句,in子句用於將建立的name變數放入namespacehash名稱空間中. 

assign指令還有如下用法:<#assign name1=value1 name2=value2 ... nameN=valueN [in namespacehash]>,這個語法可以同時建立或替換多個頂層變數,此外,還有一種複雜的用法,如果需要建立或替換的變數值是一個複雜的表示式,則可以使用如下語法格式:<#assign name [in namespacehash]>capture this</#assign>,在這個語法中,是指將assign指令的內容賦值給name變數.如下例子: 
<#assign x> 
<#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as n> 
${n} 
</#list> 
</#assign> 
${x} 
上面的程式碼將產生如下輸出:星期一 星期二 星期三 星期四 星期五 星期六 星期天 

雖然assign指定了這種複雜變數值的用法,但是我們也不要濫用這種用法,如下例子:<#assign x>Hello ${user}!</#assign>,以上程式碼改為如下寫法更合適:<#assign x="Hello ${user}!"> 

4.9 setting指令 

該指令用於設定FreeMarker的執行環境,該指令的語法格式如下:<#setting name=value>,在這個格式中,name的取值範圍包含如下幾個: 
locale:該選項指定該模板所用的國家/語言選項 
number_format:指定格式化輸出數字的格式 
boolean_format:指定兩個布林值的語法格式,預設值是true,false 
date_format,time_format,datetime_format:指定格式化輸出日期的格式 
time_zone:設定格式化輸出日期時所使用的時區 

4.10 macro , nested , return指令 

macro可以用於實現自定義指令,通過使用自定義指令,可以將一段模板片段定義成一個使用者指令,使用macro指令的語法格式如下: 
<#macro name param1 param2 ... paramN> 
... 
<#nested loopvar1, loopvar2, ..., loopvarN> 
... 
<#return> 
... 
</#macro> 
在上面的格式片段中,包含了如下幾個部分: 
name:name屬性指定的是該自定義指令的名字,使用自定義指令時可以傳入多個引數 
paramX:該屬性就是指定使用自定義指令時報引數,使用該自定義指令時,必須為這些引數傳入值 
nested指令:nested標籤輸出使用自定義指令時的中間部分 
nested指令中的迴圈變數:這此迴圈變數將由macro定義部分指定,傳給使用標籤的模板 
return指令:該指令可用於隨時結束該自定義指令. 

看如下的例子: 
<#macro book>   //定義一個自定義指令 
j2ee 
</#macro> 
<@book />    //使用剛才定義的指令 
上面的程式碼輸出結果為:j2ee 

在上面的程式碼中,可能很難看出自定義標籤的用處,因為我們定義的book指令所包含的內容非常簡單,實際上,自定義標籤可包含非常多的內容,從而可以實現更好的程式碼複用.此外,還可以在定義自定義指令時,為自定義指令指定引數,看如下程式碼: 
<#macro book booklist>     //定義一個自定義指令booklist是引數 
<#list booklist as book> 
   ${book} 
</#list> 
</#macro> 
<@book booklist=["spring","j2ee"] />   //使用剛剛定義的指令 
上面的程式碼為book指令傳入了一個引數值,上面的程式碼的輸出結果為:spring j2ee 

不僅如此,還可以在自定義指令時使用nested指令來輸出自定義指令的中間部分,看如下例子: 
<#macro page title> 
<html> 
<head> 
   <title>FreeMarker示例頁面 - ${title?html}</title> 
</head> 
<body> 
   <h1>${title?html}</h1> 
   <#nested>      //用於引入使用者自定義指令的標籤體 
</body> 
</html> 
</#macro> 
上面的程式碼將一個HTML頁面模板定義成一個page指令,則可以在其他頁面中如此page指令: 
<#import "/common.ftl" as com>     //假設上面的模板頁面名為common.ftl,匯入頁面 
<@com.page title="book list"> 
<u1> 
<li>spring</li> 
<li>j2ee</li> 
</ul> 
</@com.page> 

從上面的例子可以看出,使用macro和nested指令可以非常容易地實現頁面裝飾效果,此外,還可以在使用nested指令時,指定一個或多個迴圈變數,看如下程式碼: 
<#macro book> 
<#nested 1>      //使用book指令時指定了一個迴圈變數值 
<#nested 2> 
</#macro> 
<@book ;x> ${x} .圖書</@book> 
當使用nested指令傳入變數值時,在使用該自定義指令時,就需要使用一個佔位符(如book指令後的;x).上面的程式碼輸出文字如下: 
1 .圖書    2 .圖書 

在nested指令中使用迴圈變數時,可以使用多個迴圈變數,看如下程式碼: 
<#macro repeat count> 
<#list 1..count as x>     //使用nested指令時指定了三個迴圈變數 
   <#nested x, x/2, x==count> 
</#list> 
</#macro> 
<@repeat count=4 ; c halfc last> 
${c}. ${halfc}<#if last> Last! </#if> 
</@repeat> 
上面的輸出結果為: 
1. 0.5   2. 1   3. 1.5   4. 2 Last; 

return指令用於結束macro指令,一旦在macro指令中執行了return指令,則FreeMarker不會繼續處理macro指令裡的內容,看如下程式碼: 
<#macro book> 
spring 
<#return> 
j2ee 
</#macro> 
<@book /> 
上面的程式碼輸出:spring,而j2ee位於return指令之後,不會輸出. 

if, else, elseif 
switch, case, default, break 
list, break 
include 
Import 
compress 
escape, noescape 
assign 
global 
setting 
macro, nested, return 
t, lt, rt 
3一些常用方法或注意事項 
表示式轉換類 
數字迴圈 
對浮點取整數 
給變數預設值 
判斷物件是不是null 
常用格式化日期 
新增全域性共享變數資料模型 
直接呼叫java物件的方法 
字串處理(內建方法) 
在模板裡對sequences和hashes初始化 
註釋標誌 
sequences內建方法 
hashes內建方法 
4 freemarker在web開發中注意事項 
web中常用的幾個物件 
view中值的搜尋順序 
在模板裡ftl裡使用標籤 
如何初始化共享變數 
與webwork整合配置 
5高階方法 
自定義方法 
自定義 Transforms 

                                 
1概念 
最常用的3個概念 
sequence  序列,對應java裡的list、陣列等非鍵值對的集合 
hash      鍵值對的集合 
namespace 對一個ftl檔案的引用,利用這個名字可以訪問到該ftl檔案的資源 

2指令 
if, else, elseif 
語法 
<#if condition> 
  ... 
<#elseif condition2> 
  ... 
<#elseif condition3> 
  ... 
... 
<#else> 
  ... 
</#if> 
用例 
<#if x = 1> 
  x is 1 
</#if> 

<#if x = 1> 
  x is 1 
<#else> 
  x is not 1 
</#if> 

switch, case, default, break 
語法 
<#switch value> 
  <#case refValue1> 
    ... 
    <#break> 
  <#case refValue2> 
    ... 
    <#break> 
  ... 
  <#case refValueN> 
    ... 
    <#break> 
  <#default> 
    ... 
</#switch> 

用例 
字串 
<#switch being.size> 
  <#case "small"> 
     This will be processed if it is small 
     <#break> 
  <#case "medium"> 
     This will be processed if it is medium 
     <#break> 
  <#case "large"> 
     This will be processed if it is large 
     <#break> 
  <#default> 
     This will be processed if it is neither 
</#switch> 
數字 
<#switch x> 
  <#case x = 1> 
    1 
  <#case x = 2> 
    2 
  <#default> 
    d 
</#switch> 

如果x=1 輸出 1 2, x=2輸出 2, x=3 輸出d 

list, break 
語法 
<#list sequence as item> 
... 
<#if item = "spring"><#break></#if> 
... 
</#list> 
關鍵字 
item_index:是list當前值的下標 
item_has_next:判斷list是否還有值 

用例 
<#assign seq = ["winter", "spring", "summer", "autumn"]> 
<#list seq as x> 
  ${x_index + 1}. ${x}<#if x_has_next>,</#if> 
</#list> 

輸出 
  1. winter, 
  2. spring, 
  3. summer, 
  4. autumn  


include 
語法 
<#include filename> 
or 
<#include filename options> 
options包含兩個屬性 
encoding=”GBK” 編碼格式 
parse=true 是否作為ftl語法解析,預設是true,false就是以文字方式引入.注意在ftl檔案里布林值都是直接賦值的如parse=true,而不是parse=”true” 
用例 
/common/copyright.ftl包含內容 
Copyright 2001-2002 ${me}<br> 
All rights reserved. 
模板檔案 
<#assign me = "Juila Smith"> 
<h1>Some test</h1> 
<p>Yeah. 
<hr> 
<#include "/common/copyright.ftl" encoding=”GBK”> 
輸出結果 
<h1>Some test</h1> 
<p>Yeah. 
<hr> 
Copyright 2001-2002 Juila Smith 
All rights reserved. 

Import 
語法 
<#import path as hash> 
類似於java裡的import,它匯入檔案,然後就可以在當前檔案裡使用被匯入檔案裡的巨集元件 

用例 

假設mylib.ftl裡定義了巨集copyright那麼我們在其他模板頁面裡可以這樣使用 
<#import "/libs/mylib.ftl" as my> 

<@my.copyright date="1999-2002"/> 

"my"在freemarker裡被稱作namespace 

compress 
語法 
<#compress> 
  ... 
</#compress> 
用來壓縮空白空間和空白的行 
用例 
<#assign x = "    moo  \n\n   "> 
(<#compress> 
  1 2  3   4    5 
  ${moo} 
  test only 

  I said, test only 

</#compress>) 
輸出 
(1 2 3 4 5 
moo 
test only 
I said, test only) 
escape, noescape 
語法 
<#escape identifier as expression> 
  ... 
  <#noescape>...</#noescape> 
  ... 
</#escape> 
用例 
主要使用在相似的字串變數輸出,比如某一個模組的所有字串輸出都必須是html安全的,這個時候就可以使用該表示式 
<#escape x as x?html> 
  First name: ${firstName} 
  <#noescape>Last name: ${lastName}</#noescape> 
  Maiden name: ${maidenName} 
</#escape> 
相同表示式 
  First name: ${firstName?html} 
  Last name: ${lastName } 
  Maiden name: ${maidenName?html} 
assign 
語法 
<#assign name=value> 
or 
<#assign name1=value1 name2=value2 ... nameN=valueN> 
or 
<#assign same as above... in namespacehash> 
or 
<#assign name> 
  capture this 
</#assign> 
or 
<#assign name in namespacehash> 
  capture this 
</#assign> 
用例 
生成變數,並且給變數賦值 
給seasons賦予序列值 
<#assign seasons = ["winter", "spring", "summer", "autumn"]> 

給變數test加1 
<#assign test = test + 1> 

給my namespage 賦予一個變數bgColor,下面可以通過my.bgColor來訪問這個變數 
<#import "/mylib.ftl" as my> 
<#assign bgColor="red" in my> 

將一段輸出的文字作為變數儲存在x裡 
下面的陰影部分輸出的文字將被賦值給x 
<#assign x> 
  <#list 1..3 as n> 
    ${n} <@myMacro /> 
  </#list> 
</#assign> 
Number of words: ${x?word_list?size} 
${x} 

<#assign x>Hello ${user}!</#assign>     error 
<#assign x=” Hello ${user}!”>         true 

同時也支援中文賦值,如: 
<#assign 語法> 
  java 
</#assign> 
${語法} 
列印輸出: 
java 
global 
語法 
<#global name=value> 
or 
<#global name1=value1 name2=value2 ... nameN=valueN> 
or 
<#global name> 
  capture this 
</#global> 

全域性賦值語法,利用這個語法給變數賦值,那麼這個變數在所有的namespace中是可見的,如果這個變數被當前的assign語法覆蓋如<#global x=2> <#assign x=1> 在當前頁面裡x=2將被隱藏,或者通過${.global.x}來訪問 

setting 
語法 
<#setting name=value> 
用來設定整個系統的一個環境 
locale 
number_format 
boolean_format 
date_format, time_format, datetime_format 
time_zone 
classic_compatible 
用例 
假如當前是匈牙利的設定,然後修改成美國 
${1.2} 
<#setting locale="en_US"> 
${1.2} 
輸出 
1,2 
1.2 
因為匈牙利是採用“,”作為十進位制的分隔符,美國是用“.” 



macro, nested, return 
語法 

<#macro name param1 param2 ... paramN> 
  ... 
  <#nested loopvar1, loopvar2, ..., loopvarN> 
  ... 
  <#return> 
  ... 
</#macro> 
用例 
<#macro test foo bar="Bar" baaz=-1> 
  Test text, and the params: ${foo}, ${bar}, ${baaz} 
</#macro> 
<@test foo="a" bar="b" baaz=5*5-2/> 
<@test foo="a" bar="b"/> 
<@test foo="a" baaz=5*5-2/> 
<@test foo="a"/> 
輸出 
  Test text, and the params: a, b, 23 
  Test text, and the params: a, b, -1 
  Test text, and the params: a, Bar, 23 
  Test text, and the params: a, Bar, -1 
定義迴圈輸出的巨集 
<#macro list title items> 
  <p>${title?cap_first}: 
  <ul> 
    <#list items as x> 
      <li>${x?cap_first} 
    </#list> 
  </ul> 
</#macro> 
<@list items=["mouse", "elephant", "python"] title="Animals"/> 
輸出結果 
<p>Animals: 
  <ul> 
      <li>Mouse 
      <li>Elephant 
      <li>Python 
  </ul> 
包含body的巨集 
<#macro repeat count> 
  <#list 1..count as x> 
    <#nested x, x/2, x==count> 
  </#list> 
</#macro> 
<@repeat count=4 ; c halfc last> 
  ${c}. ${halfc}<#if last> Last!</#if> 
</@repeat> 
輸出 
1. 0.5 
  2. 1 
  3. 1.5 
  4. 2 Last! 




t, lt, rt 
語法 
<#t> 去掉左右空白和回車換行 

<#lt>去掉左邊空白和回車換行 

<#rt>去掉右邊空白和回車換行 

<#nt>取消上面的效果 


3一些常用方法或注意事項 


表示式轉換類 
${expression}計算expression並輸出 
#{ expression }數字計算#{ expression ;format}安格式輸出數字format為M和m 
M表示小數點後最多的位數,m表示小數點後最少的位數如#{121.2322;m2M2}輸出121.23 




數字迴圈 
1..5 表示從1到5,原型number..number 
對浮點取整數 
${123.23?int} 輸出123 
給變數預設值 
${var?default(“hello world<br>”)?html}如果var is null那麼將會被hello world<br>替代 

判斷物件是不是null 
    <#if mouse?exists> 
      Mouse found 
<#else> 
也可以直接${mouse?if_exists})輸出布林形 
常用格式化日期 
openingTime必須是Date型,詳細檢視freemarker文件 Reference->build-in referece->build-in for date 

${openingTime?date} 
${openingTime?date_time} 
${openingTime?time} 

新增全域性共享變數資料模型 
在程式碼裡的實現 
    cfg = Configuration.getDefaultConfiguration(); 
cfg.setSharedVariable("global", "you good"); 
頁面實現可以通過global指令,具體檢視指令裡的global部分 
直接呼叫java物件的方法 
${object.methed(args)} 

字串處理(內建方法) 
html安全輸出 
“abc<table>sdfsf”?html 
返回安全的html輸出,替換掉html程式碼 
xml安全輸出 
var?xml  
substring的用法 
<#assign user=”hello jeen”> 
${user[0]}${user[4]} 
${user[1..4]} 
輸出 : 
ho 
ello 
類似String.split的用法 
“abc;def;ghi”?split(“;”)返回sequence 
將字串按空格轉化成sequence,然後取sequence的長度 
     var?word_list  效果同 var?split(“ ”) 
var?word_list?size 

取得字串長度 
var?length 

大寫輸出字元 
var?upper_case 

小寫輸出字元 
var?lower_case 

首字元大寫 
var?cap_first 

首字元小寫 
var?uncap_first 

去掉字串前後空格 
var?trim 

每個單詞的首字元大寫 
var?capitalize 

類似String.indexof: 
“babcdabcd”?index_of(“abc”) 返回1 
“babcdabcd”?index_of(“abc”,2) 返回5 
類似String.lastIndexOf 
last_index_of和String.lastIndexOf類似,同上 

下面兩個可能在程式碼生成的時候使用(在引號前加”\”) 
j_string: 在字串引號前加”\” 
<#assign beanName = 'The "foo" bean.'> 
String BEAN_NAME = "${beanName?j_string}"; 
列印輸出: 
String BEAN_NAME = "The \"foo\" bean."; 
js_string: 
<#assign user = "Big Joe's \"right hand\"."> 
<script> 
  alert("Welcome ${user}!"); 
</script> 
列印輸出 
alert("Welcome Big Joe\'s \"right hand\"!"); 

替換字串 replace 
${s?replace(‘ba’, ‘XY’ )} 
${s?replace(‘ba’, ‘XY’ , ‘規則引數’)}將s裡的所有的ba替換成xy 規則引數包含: i r m s c f 具體含義如下: 
· i: 大小寫不區分. 
· f: 只替換第一個出現被替換字串的字串 
· r:  XY是正規表示式 
· m: Multi-line mode for regular expressions. In multi-line mode the expressions ^ and $ match just after or just before, respectively, a line terminator or the end of the string. By default these expressions only match at the beginning and the end of the entire string. 
· s: Enables dotall mode for regular expressions (same as Perl singe-line mode). In dotall mode, the expression . matches any character, including a line terminator. By default this expression does not match line terminators. 
· c: Permits whitespace and comments in regular expressions. 


在模板裡對sequences和hashes初始化 
sequences 

1. [“you”,”me”,”he”] 
2. 1..100 
3. [ {“Akey”:”Avalue”},{“Akey1”:”Avalue1”}, 
{“Bkey”:”Bvalue”},{“Bkey1”:”Bvalue1”}, 



hashes      {“you”:”a”,”me”:”b”,”he”:”c”} 


註釋標誌 
<#-- 
這裡是註釋 
--> 
舊版本的freemarker採用的是<#comment> 註釋 </#comment>方法 

sequences內建方法 
sequence?first 
返回sequence的第一個值;前提條件sequence不能是null 
sequence?last 
返回sequence最後一個值 
sequence?reverse 
反轉sequence的值 
sequence?size 
返回sequence的大小 
sequence?sort 
對sequence按裡面的物件toString()的結果進行排序 
sequence?sort_by(value) 
對sequence 按裡面的物件的屬性value進行排序 
如: sequence裡面放入的是10 個user物件,user物件裡面包含name,age等屬性 
sequence?sort_by(name) 表示所有的user按user.name進行排序 
hashes內建方法 
hash?keys 
返回hash裡的所有keys, 返回結果型別sequence 
hash?values 
返回hash裡的所有value, 返回結果型別sequence 
4 freemarker在web開發中注意事項 
freemarker與webwork整合 
web中常用的幾個物件 
Freemarker的ftl檔案中直接使用內部物件: 
${Request ["a"]} 
${RequestParameters["a"]} 
${Session ["a"]} 
${Application ["a"]} 
${JspTaglibs ["a"]} 

與webwork整合之後 通過配置的servlet 已經把request,session等物件置入了資料模型中 
在view中存在下面的物件 
  我們可以在ftl中${req}來列印req物件 
· req - the current HttpServletRequest 
· res - the current HttpServletResponse 
· stack - the current OgnlValueStack 
· ognl - the OgnlTool instance 
· webwork - an instance of FreemarkerWebWorkUtil 
· action - the current WebWork action 
· exception - optional the Exception instance, if the view is a JSP exception or Servlet exception view 
view中值的搜尋順序 
${name}將會以下面的順序查詢name值 
· freemarker variables 
· value stack 
· request attributes 
· session attributes 
· servlet context attributes 
在模板裡ftl裡使用標籤 
注意,如果標籤的屬性值是數字,那麼必須採用nubmer=123方式給屬性賦值 
JSP頁面 
<%@page contentType="text/html;charset=ISO-8859-2" language="java"%> 
<%@taglib uri="/WEB-INF/struts-html.tld" prefix="html"%> 
<%@taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%> 

<html> 
  <body> 
    <h1><bean:message key="welcome.title"/></h1> 
    <html:errors/> 
    <html:form action="/query"> 
      Keyword: <html:text property="keyword"/><br> 
      Exclude: <html:text property="exclude"/><br> 
      <html:submit value="Send"/> 
    </html:form> 
  </body> 
</html> 
模板ftl頁面 
<#assign html=JspTaglibs["/WEB-INF/struts-html.tld"]> 
<#assign bean=JspTaglibs["/WEB-INF/struts-bean.tld"]> 

<html> 
  <body> 
    <h1><@bean.message key="welcome.title"/></h1> 
    <@html.errors/> 
    <@html.form action="/query"> 
      Keyword: <@html.text property="keyword"/><br> 
      Exclude: <@html.text property="exclude"/><br> 
      <@html.submit value="Send"/> 
    </@html.form> 
  </body> 
</html> 


如何初始化共享變數 
1. 初始化全域性共享資料模型 
freemark在web上使用的時候對共享資料的初始化支援的不夠,不能在配置初始化的時候實現,而必須通過ftl檔案來初始化全域性變數。這是不能滿主需求的,我們需要在servlet init的時候留出一個介面來初始化系統的共享資料 
具體到和webwork整合,因為本身webwork提供了整合servlet,如果要增加全域性共享變數,可以通過修改 com.opensymphony.webwork.views.freemarker.FreemarkerServlet來實現,我們可以在這個 servlet初始化的時候來初始化全域性共享變數 
與webwork整合配置 
配置web.xml 
<servlet> 
    <servlet-name>freemarker</servlet-name> 
    <servlet-class>com.opensymphony.webwork.views.freemarker.FreemarkerServlet</servlet-class> 
    <init-param> 
      <param-name>TemplatePath</param-name> 
<param-value>/</param-value> 
<!—模板載入資料夾,這裡相對context root,遞迴獲取該資料夾下的所有模板--> 
    </init-param> 
    <init-param> 
      <param-name>NoCache</param-name> <!—是否對模板快取--> 
      <param-value>true</param-value> 
    </init-param> 
    <init-param> 
      <param-name>ContentType</param-name> 
      <param-value>text/html</param-value> 
    </init-param> 
    <init-param> 
<param-name>template_update_delay</param-name> 
<!—模板更新時間,0表示每次都更新,這個適合開發時候--> 
      <param-value>0</param-value> 
    </init-param> 
    <init-param> 
      <param-name>default_encoding</param-name> 
      <param-value>GBK</param-value> 
    </init-param> 
    <init-param> 
      <param-name>number_format</param-name> 
      <param-value>0.##########</param-value><!—數字顯示格式--> 
    </init-param> 
    <load-on-startup>1</load-on-startup> 
  </servlet> 
  <servlet-mapping> 
    <servlet-name>freemarker</servlet-name> 
    <url-pattern>*.ftl</url-pattern> 
  </servlet-mapping> 

5高階方法 
自定義方法 
${timer("yyyy-MM-dd H:mm:ss", x)} 
${timer("yyyy-MM-dd ", x)} 

在模板中除了可以通過物件來呼叫方法外(${object.methed(args)})也可以直接呼叫java實現的方法,java類必須實現介面TemplateMethodModel的方法exec(List args). 下面以把毫秒的時間轉換成按格式輸出的時間為例子 
public class LongToDate implements TemplateMethodModel { 
   
public TemplateModel exec(List args) throws TemplateModelException { 
SimpleDateFormat mydate = new SimpleDateFormat((String) args.get(0))); 
        return mydate.format(new Date(Long.parseLong((String)args.get(1))); 
    } 

將LongToDate物件放入到資料模型中 
root.put("timer", new IndexOfMethod()); 
ftl模板裡使用 
<#assign x = "123112455445"> 
${timer("yyyy-MM-dd H:mm:ss", x)} 
${timer("yyyy-MM-dd ", x)} 

輸出 
2001-10-12 5:21:12 
2001-10-12 

自定義 Transforms 
實現自定義的<@transform>文字或表示式</@transform>的功能,允許對中間的最終文字進行解析轉換 

例子:實現<@upcase>str</@upcase> 將str轉換成STR 的功能 

程式碼如下: 
import java.io.*; 
import java.util.*; 
import freemarker.template.TemplateTransformModel; 

class UpperCaseTransform implements TemplateTransformModel { 

    public Writer getWriter(Writer out, Map args) { 
        return new UpperCaseWriter(out); 
    } 

    private class UpperCaseWriter extends Writer { 
      
        private Writer out; 
          
        UpperCaseWriter (Writer out) { 
            this.out = out; 
        } 

        public void write(char[] cbuf, int off, int len) 
                throws IOException { 
            out.write(new String(cbuf, off, len).toUpperCase()); 
        } 

        public void flush() throws IOException { 
            out.flush(); 
        } 

        public void close() { 
        } 
    } 

然後將此物件put到資料模型中 
root.put("upcase", new UpperCaseTransform()); 

在view(ftl)頁面中可以如下方式使用 

<@upcase> 
hello world 
</@upcase> 

列印輸出: 
HELLO WORLD

相關文章