FreeMarker模板語言開發(整理版)
FreeMarker語言
FreeMarker語言概述
FreeMarker是一個模板引擎,一個基於模板生成文字輸出的通用工具,使用純Java編寫。
FreeMarker被設計用來生成HTML Web頁面,特別是基於MVC模式的應用程式
雖然FreeMarker具有一些程式設計的能力,但通常由Java程式準備要顯示的資料,由FreeMarker生成頁面,通過模板顯示準備的資料(如下圖)
FreeMarker不是一個Web應用框架,而適合作為Web應用框架一個元件。
FreeMarker與容器無關,因為它並不知道HTTP或Servlet;FreeMarker同樣可以應用於非Web應用程式環境。
FreeMarker更適合作為Model2框架(如Struts)的檢視元件,你也可以在模板中使用JSP標記庫。
FreeMarker是免費的。
FreeMarker特性
通用目標
能夠生成各種文字:HTML、XML、RTF、Java原始碼等等
易於嵌入到你的產品中:輕量級;不需要Servlet環境
外掛式模板載入器:可以從任何源載入模板,如本地檔案、資料庫等等
你可以按你所需生成文字:儲存到本地檔案;作為Email傳送;從Web應用程式傳送它返回給Web瀏覽器
強大的模板語言
所有常用的指令:include、if/elseif/else、迴圈結構
在模板中建立和改變變數
幾乎在任何地方都可以使用複雜表示式來指定值
命名的巨集,可以具有位置引數和巢狀內容
名字空間有助於建立和維護可重用的巨集庫,或者將一個大工程分成模組,而不必擔心名字衝突
輸出轉換塊:在巢狀模板片段生成輸出時,轉換HTML轉義、壓縮、語法高亮等等;你可以定義自己的轉換
通用資料模型
FreeMarker不是直接反射到Java物件,Java物件通過外掛式物件封裝,以變數方式在模板中顯示
你可以使用抽象(介面)方式表示物件(JavaBean、XML文件、SQL查詢結果集等等),告訴模板開發者使用方法,使其不受技術細節的打擾
為Web準備
在模板語言中內建處理典型Web相關任務(如HTML轉義)的結構
能夠整合到Model2 Web應用框架中作為JSP的替代
支援JSP標記庫
為MVC模式設計:分離視覺化設計和應用程式邏輯;分離頁面設計員和程式設計師
智慧的國際化和本地化
字符集智慧化(內部使用UNICODE)
數字格式本地化敏感
日期和時間格式本地化敏感
非US字符集可以用作標識(如變數名)
多種不同語言的相同模板
強大的XML處理能力
<#recurse> 和<#visit>指令(2.3版本)用於遞迴遍歷XML樹。在模板中清楚和直覺的訪問XML物件模型。開源論壇 JForum 就是使用了 FreeMarker 做為頁面模板。
第一個FreeMarker程式
1、建立一個普通的java專案:testFreeMarker
2、引入freemarker.jar包
3、在專案目錄下建立模板目錄:templates
4、在templates目錄下,建立a.ftl模板檔案,內容如下:
Hello world,${user},今天天氣不錯! |
5、建立com.sxt.test.freemarker包,然後建立Testjava檔案,內容如下:
package com.thaifintech.test.freemarker;
import java.io.File; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.HashMap; import java.util.Map;
import freemarker.template.Configuration; import freemarker.template.DefaultObjectWrapper; import freemarker.template.Template;
public class Test1 { public static void main(String[] args) throws Exception { //建立Freemarker配置例項 Configuration cfg = new Configuration();
cfg.setDirectoryForTemplateLoading(new File("templates"));
//建立資料模型 Map root = new HashMap(); root.put("user", "老高");
//載入模板檔案 Template t1 = cfg.getTemplate("a.ftl");
//顯示生成的資料,//將合併後的資料列印到控制檯 Writer out = new OutputStreamWriter(System.out); t1.process(root, out); out.flush();
//顯示生成的資料,//將合併後的資料直接返回成字串! // StringWriter out = new StringWriter(); // t1.process(root, out); // out.flush(); // String temp = out.toString(); // System.out.println(temp); } } |
6、編譯和執行Testjava檔案,控制檯列印:
資料型別
一、 直接指定值
直接指定值可以是字串、數值、布林值、集合及Map物件。
1. 字串
直接指定字串值使用單引號或雙引號限定。字串中可以使用轉義字元”\"。如果字串內有大量的特殊字元,則可以在引號的前面加上一個字母r,則字串內的所有字元都將直接輸出。
2. 數值
數值可以直接輸入,不需要引號。FreeMarker不支援科學計數法。
3. 布林值
直接使用true或false,不使用引號。
4. 集合
集合用中括號包括,集合元素之間用逗號分隔。
使用數字範圍也可以表示一個數字集合,如1..5等同於集合[1, 2, 3, 4, 5];同樣也可以用5..1來表示[5, 4, 3, 2, 1]。
5. Map物件
Map物件使用花括號包括,Map中的key-value對之間用冒號分隔,多組key-value對之間用逗號分隔。
注意:Map物件的key和value都是表示式,但key必須是字串。
6. 時間物件
root.put("date1", new Date());
${date1?string("yyyy-MM-dd HH:mm:ss")}
7. JAVABEAN的處理
Freemarker中對於javabean的處理跟EL表示式一致,型別可自動轉化!非常方便!
二、 輸出變數值
FreeMarker的表示式輸出變數時,這些變數可以是頂層變數,也可以是Map物件的變數,還可以是集合中的變數,並可以使用點(.)語法來訪問Java物件的屬性。
1. 頂層變數
所謂頂層變數就是直接放在資料模型中的值。輸出時直接用${variableName}即可。
2. 輸出集合元素
可 以根據集合元素的索引來輸出集合元素,索引用中括號包括。如: 輸出[“1”, “2”, “3”]這個名為number的集合,可以用${number[0]}來輸出第一個數字。FreeMarker還支援用number[1..2]來表示原 集合的子集合[“2”, “3”]。
3. 輸出Map元素
對於JavaBean例項,FreeMarker一樣把它看作屬性為key,屬性值為value的Map物件。
輸出Map物件時,可以使用點語法或中括號語法,如下面的幾種寫法的效果是一樣的:
book.author.name
book.author["name"]
book["author"].name
book["author"]["name"]
使用點語法時,變數名字有和頂層變數一樣的限制,但中括號語法沒有任何限制。
三、字串操作
1. 字串連線
字串連線有兩種語法:
(1) 使用${..}或#{..}在字串常量內插入表示式的值;
(2) 直接使用連線運算子“+”連線字串。
如,下面兩種寫法等效:
${"Hello, ${user}"}
${"Hello, " + user + "!"}
有一點需要注意: ${..}只能用於文字部分作為插值輸出,而不能用於比較等其他用途,如:
<#if ${isBig}>Wow!</#if>
<#if "${isBig}">Wow!</#if>
應該寫成:
<#if isBig>Wow!</#if>
2. 擷取子串
擷取子串可以根據字串的索引來進行,如果指定一個索引值,則取得字串該索引處的字元;如果指定兩個索引值,則擷取兩個索引中間的字串子串。如:
<#assign number="01234">
${number[0]} <#-- 輸出字元0 -->
${number[0..3]} <#-- 輸出子串“0123” -->
四、集合連線操作
連線集合的運算子為“+”
五、Map連線操作
Map連線操作的運算子為“+”
六、算術運算子
FreeMarker表示式中支援“+”、“-”、“*”、“/”、“%”運算子。
七、比較運算子
表示式中支援的比較運算子有如下幾種:
1. =(或者==): 判斷兩個值是否相等;
2. !=: 判斷兩個值是否不相等;
注: =和!=可以用作字串、數值和日期的比較,但兩邊的資料型別必須相同。而且FreeMarker的比較是精確比較,不會忽略大小寫及空格。
3. >(或者gt): 大於
4. >=(或者gte): 大於等於
5. <(或者lt): 小於
6. <=(或者lte): 小於等於
注: 上面這些比較運算子可以用於數字和日期,但不能用於字串。大部分時候,使用gt比>有更好的效果,因為FreeMarker會把>解釋成標籤的結束字元。可以使用括號來避免這種情況,如:<#if (x>y)>。
if else 語句測試:
<#if num0 gt 18> <#--不是使用>,大部分時候,freemarker會把>解釋成標籤結束! -->
及格!
<#else>
不及格!
</#if>
root.put("num0", 18);
八、邏輯運算子
1. &&: 邏輯與;
2. ||: 邏輯或;
3. !: 邏輯非
邏輯運算子只能用於布林值。
九、內建函式
FreeMarker提供了一些內建函式來轉換輸出,可以在任何變數後緊跟?,?後緊跟內建函式,就可以通過內建函式來轉換輸出變數。
字串相關常用的內建函式:
1. html: 對字串進行HTML編碼;
2. cap_first: 使字串第一個字母大寫;
3. lower_case: 將字串轉成小寫;
4. upper_case: 將字串轉成大寫;
集合相關常用的內建函式:
1. size: 獲得集合中元素的個數;
數字值相關常用的內建函式:
1. int: 取得數字的整數部分。
舉例:
root.put("htm2", "<b>粗體</b>");
內建函式:
${htm2?html}
十、空值處理運算子
FreeMarker的變數必須賦值,否則就會丟擲異常。而對於FreeMarker來說,null值和不存在的變數是完全一樣的,因為FreeMarker無法理解null值。
FreeMarker提供兩個運算子來避免空值:
1. !: 指定缺失變數的預設值;
2. ??:判斷變數是否存在。
!運算子有兩種用法:variable!或variable!defaultValue。第一種用法不給變數指定預設值,表明預設值是空字串、長度為0的集合、或長度為0的Map物件。
使用!運算子指定預設值並不要求預設值的型別和變數型別相同。
測試空值處理:
<#-- ${sss} 沒有定義這個變數,會報異常! -->
${sss!} <#--沒有定義這個變數,預設值是空字串! -->
${sss!"abc"} <#--沒有定義這個變數,預設值是字串abc! -->
??運算子返回布林值,如:variable??,如果變數存在,返回true,否則返回false。
資料型別常見示例
直接指定值
- 字串 : "Foo"或 者'Foo'或"It's \"quoted\""或r"C:\raw\string"
- 數字:123.45
- 布林值:true, false
- 序列:["foo", "bar", 123.45], 1..100
- 雜湊表:{"name":"green mouse", "price":150}
- 檢索變數 頂層變數:user
- 從雜湊表中檢索資料:user.name, user[“name”]
- 從序列中檢索:products[5]
- 特殊變數:.main
- 字串操作
- 插值(或連線):"Hello ${user}!"(或"Free" + "Marker")
- 獲取一個字元:name[0]
- 序列操作
- 連線:users + ["guest"]
- 序列切分:products[10..19] 或 products[5..]
- 雜湊表操作
- 連線:passwords + {"joe":"secret42"}
- 算數運算: (x * 1.5 + 10) / 2 - y % 100
- 比 較 運 算 : x == y, x != y, x < y, x > y, x >= y, x <= y, x < y, 等等
- 邏輯操作:!registered && (firstVisit || fromEurope)
- 內建函式:name?upper_case
- 方法呼叫:repeat("What", 3)
- 處理不存在的值
- 預設值:name!"unknown" 或者(user.name)!"unknown" 或者name! 或者 (user.name)!
- 檢測不存在的值:name?? 或者(user.name)??
參考:運算子的優先順序
模板開發語句
最簡單的模板是普通 HTML 檔案(或者是其他任何文字檔案—FreeMarker 本身不屬於HTML)。當客戶端訪問頁面時,FreeMarker 要傳送 HTML 程式碼至客戶端瀏覽器端顯示。如果想要頁面動起來,就要在 HTML 中放置能被 FreeMarker 所解析的特殊部分。
${…}:FreeMarker 將會輸出真實的值來替換花括號內的表示式,這樣的表示式被稱為
interpolations 插值,可以參考第上面示例的內容。
FTL tags 標籤(FreeMarker 模板的語言標籤):FTL 標籤和 HTML 標籤有一點相似,但是它們是 FreeMarker 的指令而且是不會直接輸出出來的東西。這些標籤的使用一般以符號#開頭。(使用者自定義的 FTL 標籤使用@符號來代替#,但這是更高階的主題內容了,後面會詳細地討論)
Comments 註釋:FreeMarker 的註釋和 HTML 的註釋相似,但是它用<#--和-->來分隔的。任何介於這兩個分隔符(包含分隔符本身)之間內容會被 FreeMarker 忽略,就不會
輸出出來了。
其他任何不是 FTL 標籤,插值或註釋的內容將被視為靜態文字,這些東西就不會被
FreeMarker 所解析,會被按照原樣輸出出來。
directives指令:就是所指的 FTL 標籤。這些指令在 HTML 的標籤(如<table>和
</table>)和 HTML 元素(如 table 元素)中的關係是相同的。(如果現在你還不能區
分它們,那麼把“FTL 標籤”和“指令”看做是同義詞即可。)
if指令
root.put("random", new Random().nextInt(100)); |
------------------------------------------------ if語句測試: ${user}是<#if user=="老高">我們的老師</#if> ------------------------------------------------ if else 語句測試: <#if num0 gt 18> <#--不是使用>,大部分時候,freemarker會把>解釋成標籤結束! --> 及格! <#else> 不及格! </#if> --------------------------------------------------- if else if else語句測試: <#if random gte 90> 優秀! <#elseif random gte 80> 良好! <#else> 一般! </#if> ---------------------------------------------------- |
list指令
List list = new ArrayList(); list.add(new Address("中國","北京")); list.add(new Address("中國","上海")); list.add(new Address("美國","紐約")); root.put("lst", list); |
測試list指令: <#list lst as dizhi > <b>${dizhi.country}</b> <br/> </#list>
思考問題:<c:forEach> status屬性。在此處如何實現?
|
控制檯列印: 測試list語句: <b>中國</b> <br/> <b>中國</b> <br/> <b>美國</b> <br/> |
include指令
增加被包含檔案,放於templates目錄下:
檔案內容如下:
模板檔案中程式碼如下:
測試include指令: <#include "included.txt" /> |
自定義指令(macro指令)
<#macro m1> <#--定義指令m1 --> <b>aaabbbccc</b> <b>dddeeefff</b> </#macro> |
<@m1 /><@m1 /> <#--呼叫上面的巨集指令 --> |
定義帶參的巨集指令:
<#macro m2 a b c > ${a}--${b}--${c} </#macro> |
<@m2 a="老高" b="老張" c="老馬" /> |
nested指令:
<#macro border> <table border=4 cellspacing=0 cellpadding=4><tr><td> <#nested> </td></tr></table> </#macro> |
<@border >表格中的內容!</@border> |
巨集指令中,有沒有類似於方法的返回值?
名稱空間
當執行 FTL 模板時,就會有使用 assign 和 macro 指令建立的變數的集合(可能是空的),可以從前一章節來看如何使用它們。像這樣的變數集合被稱為 namespace 名稱空間。在簡單的情況下可以只使用一個名稱空間,稱之為 main namespace 主名稱空間。因為通常只使用本頁上的名稱空間,所以就沒有意識到這點。
如果想建立可以重複使用的巨集,函式和其他變數的集合,通常用術語來說就是引用
library 庫。使用多個名稱空間是必然的。只要考慮你在一些專案中,或者想和他人共享使用的時候,你是否有一個很大的巨集的集合。但要確保庫中沒有巨集(或其他變數)名和資料模型中變數同名,而且也不能和模板中引用其他庫中的變數同名。通常來說,變數因為名稱衝突也會相互衝突。所以要為每個庫中的變數使用不同的名稱空間。
定義b.ftl檔案:
<#macro copyright date> <p>Copyright (C) ${date} 泰中.</p> </#macro> <#assign mail = "thaifintech@163.com"> |
在a.ftl檔案中引入b.ftl,從而可以使用b.ftl中定義的巨集和變數:
測試名稱空間: <#import "b.ftl" as bb /> <@bb.copyright date="2010-2011" /> ${bb.mail} <#assign mail="[email protected]com" /> ${mail} <#assign mail="[email protected]com" in bb /> ${bb.mail} |
執行後,控制檯列印:
測試名稱空間: <p>Copyright (C) 2017-2018 泰中.</p> |
名稱空間命名規則
如果你為 Example 公司工作,它們擁有 www.example.com 網的主頁,你的工作是開發
一個部件庫,那麼要引入你所寫的 FTL 的路徑應該是:
/lib/example.com/widget.ftl
注意到 www 已經被省略了。第三次路徑分割後的部分可以包含子目錄,可以像下面這
樣寫:
/lib/example.com/commons/string.ftl
一個重要的規則就是路徑不應該包含大寫字母,為了分隔詞語,使用下劃線_,就像
wml_form(而不是 wmlForm)。
如果你的工作不是為公司或組織開發庫,也要注意,你應該使用專案主頁的 URL,比如
/lib/example.sourceforge.net/example.ftl或/lib/geocities.com/jsmith/example.ftl。
在Servlet中使用Freemarker
參考Freemarker包中example目錄下webapp1專案!
struts2中整合FreeMarker
1.解壓struts2-core-X.X.X.jar檔案,把在META-INF資料夾下面的struts-tags.tld檔案複製到WEB-INF資料夾下。 將freemark的jar匯入到工程中
2.在web.xml檔案中配置freemark同時啟動JSPSupportServlet.程式碼如下:
<servlet>
<servlet-name>freemarker</servlet-name>
<servlet-class>
freemarker.ext.servlet.FreemarkerServlet
</servlet-class>
<!--下面的配置freemarke的ftl檔案的位置 -->
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/</param-value>
</init-param>
<!-- 是否和伺服器(tommcat)一起啟動。-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>
<servlet>
<!-- define a JspSupportServlet Object -->
<servlet-name>JspSupportServlet</servlet-name>
<servlet-class>org.apache.struts2.views.JspSupportServlet</servlet-class>
<!-- setting JspSupportServlet auto start -->
<load-on-startup>1</load-on-startup>
</servlet>
3.在FreeMarker模板中使用assign指令匯入標籤庫。程式碼如下
<#assign s=JspTaglibs["/WEB-INF/struts-tags.tld"] /> 注:這裡我把struts-tags.tld放在WEB-INF下面
4.現在我們可以在FreeMarker模板中使用標籤了。