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程序
- 建立一個普通的java項目:testFreeMarker
- 引入freemarker.jar包
- 在項目目錄下建立模板目錄:templates
- 在templates目錄下,建立a.ftl模板文件,內容如下:
你好啊,${user},今天你的精神不錯!
- 建立com.sxt.test.freemarker包,然后建立Test1.java文件,內容如下:
package com.sxt.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); }
}
- 編譯和運行Test1.java文件,控制臺打?。?/li>
你好啊,老高,今天你精神不錯!
數據類型
一、 直接指定值
直接指定值可以是字符串、數值、布爾值、集合及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 + "!"}
有一點需要注意: ${..}只能用于文本部分作為插值輸出,而不能用于比較等其他用途,如:
<#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表達式中支持“+”、“-”、“*”、“/”、“%”運算符。
七、比較運算符
表達式中支持的比較運算符有如下幾種:
- =(或者==): 判斷兩個值是否相等;
- !=: 判斷兩個值是否不相等;
注: =和!=可以用作字符串、數值和日期的比較,但兩邊的數據類型必須相同。而且FreeMarker的比較是精確比較,不會忽略大小寫及空格。 (或者gt): 大于
=(或者gte): 大于等于
- <(或者lt): 小于
- <=(或者lte): 小于等于
注: 上面這些比較運算符可以用于數字和日期,但不能用于字符串。大部分時候,使用gt比>有更好的效果,因為FreeMarker會把>解釋成標簽的結束字符??梢允褂美ㄌ杹肀苊膺@種情況,如:
<#if (x>y)>。
if else 語句測試:
<#if num0 gt 18> <#--不是使用>,大部分時候,freemarker會把>解釋成標簽結束! -->
及格!
<#else>
不及格!
</#if>
root.put("num0", 18);
八、邏輯運算符
- &&: 邏輯與;
- ||: 邏輯或;
- !: 邏輯非
邏輯運算符只能用于布爾值。
九、內建函數
FreeMarker提供了一些內建函數來轉換輸出,可以在任何變量后緊跟?,?后緊跟內建函數,就可以通過內建函數來轉換輸出變量。
字符串相關常用的內建函數:
- html: 對字符串進行HTML編碼;
- cap_first: 使字符串第一個字母大寫;
- lower_case: 將字符串轉成小寫;
- upper_case: 將字符串轉成大寫;
集合相關常用的內建函數:
- size: 獲得集合中元素的個數;
數字值相關常用的內建函數:
- int: 取得數字的整數部分。
舉例:
root.put("htm2", "<b>粗體</b>");
內建函數:
${htm2?html}
十、空值處理運算符
FreeMarker的變量必須賦值,否則就會拋出異常。而對于FreeMarker來說,null值和不存在的變量是完全一樣的,因為FreeMarker無法理解null值。
FreeMarker提供兩個運算符來避免空值:
- !: 指定缺失變量的默認值;
- ??:判斷變量是否存在。
!運算符有兩種用法: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}--$--${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 = "bjsxt@163.com">
在a.ftl文件中引入b.ftl,從而可以使用b.ftl中定義的宏和變量:
測試命名空間:
<#import "b.ftl" as bb />
<@bb.copyright date="2010-2011" />
${bb.mail}
<#assign mail="my@163.com" />
${mail}
<#assign mail="my@163.com" in bb />
${bb.mail}
執行后,控制臺打?。?br> 測試命名空間:
<p>Copyright (C) 2010-2011 北京尚學堂.</p>
bjsxt@163.com
my@163.com
my@163.com
命名空間命名規則
如果你為 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>
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/</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>
<servlet>
<servlet-name>JspSupportServlet</servlet-name>
<servlet-class>org.apache.struts2.views.JspSupportServlet</servlet-class>
<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模板中使用標簽了。
-------------------------------------
測試
list索引_index
<#list list as city>
${city}
${city_index}
<#if city_index==1>
<#break>
</#if>
</#list>
測試list中國_has_next
<#list list as city>
${city}
${city_index}
<#if city_has_next>
我有下一項!------- ${city_index}
</#if>
</#list>