天天看點

FreeMarker常用功能

CopyFrom:http://demojava.iteye.com/blog/800204

FreeMarker是一個用Java語言編寫的模闆引擎,它基于模闆來生成文本輸出。FreeMarker與Web容器無關,即在Web運作時,它并不知道Servlet或HTTP。它不僅可以用作表現層的實作技術,而且還可以用于生成XML,JSP或Java 等。

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}

 輸出結果是:

 $42.00

 42

 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>

 輸出結果是:星期一 星期二 星期三 星期四 星期五 星期六 星期天

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函數可對數值取整,如:

 ${ (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>...

 <#else> ...

 </#if>

例子如下:

 <#assign age=23>

 <#if (age>60)>老年人

 <#elseif (age>40)>中年人

 <#elseif (age>20)>青年人

 <#else> 少年人

 輸出結果是:青年人

 上面的代碼中的邏輯表達式用括号括起來主要是因為裡面有>符号,由于FreeMarker會将>符号當成标簽的結束字元,可能導緻程式出錯,為了避免這種情況,我們應該在凡是出現這些符号的地方都使用括号.

4.2 switch , case , default , break指令

這些指令顯然是分支指令,作用類似于Java的switch語句,switch指令的文法結構如下:

 <#switch value>

 <#case refValue>...<#break>

 <#default>...

 </#switch>

4.3 list, break指令

list指令是一個疊代輸出指令,用于疊代輸出資料模型中的集合,list指令的文法格式如下:

 <#list sequence as item>

 ...

 上面的文法格式中,sequence就是一個集合對象,也可以是一個表達式,但該表達式将傳回一個集合對象,而item是一個任意的名字,就是被疊代輸出的集合元素.此外,疊代集合對象時,還包含兩個特殊的循環變量:

 item_index:目前變量的索引值

 item_has_next:是否存在下一個對象

 也可以使用<#break>指令跳出疊代

 ${x_index + 1}.${x}<#if x_has_next>,</if>

 <#if x="星期四"><#break></#if>

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}

 </#noparse>

 輸出如下:

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}

 上面的代碼等同于:

 First name:${firstName?html}

 Last name:${lastName?html}

 Maiden name:${maidenName?html}

escape指令在解析模闆時起作用而不是在運作時起作用,除此之外,escape指令也嵌套使用,子escape繼承父escape的規則,如下例子:

 Customer Name:${customerName}

 Items to ship;

 <#escape x as itemCodeToNameMap[x]>

    ${itemCode1}

    ${itemCode2}

    ${itemCode3}

    ${itemCode4}

 上面的代碼類似于:

 Customer Name:${customerName?html}

 ${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}

 </#assign>

 上面的代碼将産生如下輸出:星期一 星期二 星期三 星期四 星期五 星期六 星期天

雖然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

 <@book />    //使用剛才定義的指令

 上面的代碼輸出結果為:j2ee

在上面的代碼中,可能很難看出自定義标簽的用處,因為我們定義的book指令所包含的内容非常簡單,實際上,自定義标簽可包含非常多的内容,進而可以實作更好的代碼複用.此外,還可以在定義自定義指令時,為自定義指令指定參數,看如下代碼:

 <#macro book booklist>     //定義一個自定義指令booklist是參數

 <#list booklist as book>

    ${book}

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

 上面的代碼将一個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>

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

 <@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指令裡的内容,看如下代碼:

 spring

 <@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 x = 1>

   x is 1

<#if x = 1>

   x is not 1

   <#case refValue1>

     ...

     <#break>

   <#case refValue2>

   <#case refValueN>

   <#default>

用例

 字元串

 <#switch being.size>

   <#case "small">

      This will be processed if it is small

      <#break>

   <#case "medium">

      This will be processed if it is medium

   <#case "large">

      This will be processed if it is large

      This will be processed if it is neither

 數字

 <#switch x>

   <#case x = 1>

     1

   <#case x = 2>

     2

     d

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

list, break

 <#if item = "spring"><#break></#if>

 關鍵字

 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>

輸出

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

輸出結果

 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>

 主要使用在相似的字元串變量輸出,比如某一個子產品的所有字元串輸出都必須是html安全的,這個時候就可以使用該表達式

   First name: ${firstName}

   <#noescape>Last name: ${lastName}</#noescape>

   Maiden name: ${maidenName}

 相同表達式 

  First name: ${firstName?html}

   Last name: ${lastName }

   Maiden name: ${maidenName?html}

 assign

 <#assign name=value>

 <#assign name1=value1 name2=value2 ... nameN=valueN>

 <#assign same as above... in namespacehash>

 <#assign name>

   capture this

 <#assign name in namespacehash>

 生成變量,并且給變量指派

 給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

   <#list 1..3 as n>

     ${n} <@myMacro />

   </#list>

 Number of words: ${x?word_list?size}

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

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

同時也支援中文指派,如:

 <#assign 文法>

   java

 ${文法}

 列印輸出:

 java

 global

 <#global name=value>

 <#global name1=value1 name2=value2 ... nameN=valueN>

 <#global name>

 </#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 name param1 param2 ... paramN>

   <#nested loopvar1, loopvar2, ..., loopvarN>

   <#return>

 <#macro test foo bar="Bar" baaz=-1>

   Test text, and the params: ${foo}, ${bar}, ${baaz}

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

 <@list items=["mouse", "elephant", "python"] title="Animals"/>

 輸出結果 

<p>Animals:

       <li>Mouse

       <li>Elephant

       <li>Python

 包含body的宏

   <#list 1..count as x>

     <#nested x, x/2, x==count>

   ${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

 也可以直接${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>

       <param-name>NoCache</param-name> <!—是否對模闆緩存-->

       <param-value>true</param-value>

       <param-name>ContentType</param-name>

       <param-value>text/html</param-value>

 <param-name>template_update_delay</param-name>

 <!—模闆更新時間,0表示每次都更新,這個适合開發時候-->

       <param-value>0</param-value>

       <param-name>default_encoding</param-name>

       <param-value>GBK</param-value>

       <param-name>number_format</param-name>

       <param-value>0.##########</param-value><!—數字顯示格式-->

     <load-on-startup>1</load-on-startup>

   </servlet>

   <servlet-mapping>

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

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() {

         }

     }

 }  
      

繼續閱讀