複製鏈接
請複製以下鏈接發送給好友

freemarker

鎖定
FreeMarker是一款模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁、電子郵件配置文件源代碼等)的通用工具。 它不是面向最終用户的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。
FreeMarker是免費的,基於Apache許可證2.0版本發佈。其模板編寫為FreeMarker Template Language(FTL),屬於簡單、專用的語言。需要準備數據在真實編程語言中來顯示,比如數據庫查詢和業務運算, 之後模板顯示已經準備好的數據。在模板中,主要用於如何展現數據, 而在模板之外注意於要展示什麼數據 [1] 
中文名
福瑞馬克
外文名
FreeMarker
類    別
模板引擎
特    點
通用性高,模板語言強大
最新版本
FreeMarker 2.3.23

freemarker發展歷史

LOGO LOGO
FreeMarker最初的設計,是被用來在MVC模式的Web開發框架中生成HTML頁面的,沒有被綁定到ServletHTML或任意Web相關的東西上,它也可以用於非Web應用環境中。
1999年末,FreeMarker的第一個版本出現在SourceForge網站上,它最初是由Benjamin Geer和Mike Bayer編寫,他們定義了FreeMarker最基本的語法。FreeMarker 1獲得了LGPL(寬通用公共許可證)的許可,其版權歸屬於Benjamin Geer。此外,Nicholas Cull、Holger Arendt等人對該項目也做出了主要貢獻。
在2002年初,Jonathan Revusky用JavaCC重寫了FreeMarker的核心代碼(語法編譯),雖然對FreeMarker 1儘量做到向後兼容,但幾乎是完全重寫了。Attila Szegedi對FreeMarker 2也有重要影響,除了重構和優化一些核心的API應用程序編程接口),Attila還作為主要編寫者實現了FreeMarker對日期、時間的支持,寫出的freemarker.ext*包完成對javabeanJython和XML的映射,以及HTTP servletJSP和Ant的集成。Dániel Dékány主要負責文檔以及項目的維護(截至2011年,Dániel Dékány仍是該項目的主要維護者) [2] 
2002年3月18日,FreeMarker的第一個發佈候選版2.0 RC1發佈 [3]  ,又經過了2個候選版的BUG修復之後,正式版的Free Marker2.0於2002年4月18日發佈 [4]  。2002年10月17日,FreeMarker 2.1 發佈,該版本並不能與2.0版本兼容,所以使用者如果不是新建工程的話,需要重新審視已有的代碼和模版 [5] 
由於項目沒有法律實體,FreeMarker的2.0.x和2.1.x的版權仍歸屬Benjamin Geer。而在2002年12月製作2.2版本時,Benjamin Geer出於對自由開源許可的理解,將代碼庫版權轉給Visigoth Software Society(西班牙的一個非營利性軟件協會)和共同創辦人Jonathan Revusky [2] 
2003年3月27日,FreeMarker 2.2 發佈,這個版本引入了一些非常重要的新特性,但是有一些功能卻不能逆向兼容 [6]  。在2.2的版本中,可能最重要的新特性就是namespace支持,這使得FreeMarker成為了合適大規模項目的工具,因為它允許不同頁面分享的變量沒有任何名稱空間衝突。同時,宏也變得更加強大,因為他們可以調用可選目標,並且宏現作為一流的變量,可以傳遞給其他宏使用。此外2.2中另一個吸引人的特性為,FreeMarker可以利用由第三方所寫的JSP標記庫 [7]  。在此之後,2.3版本之前,共更新了8個版本 [6] 
2004年6月15日,FreeMarker 2.3 發佈,此版本對2.2系列進行了質量上的改進,以及引入了大量的新功能。最主要的改進點在於可以定義函數(方法)模版,插入字符串變量,支持宏參數和更為智能的默認對象包裝。但2.3並不支持2.2.x的向後兼容,所以僅供新項目使用 [8] 
2005年1月4日的2.3.1版本到10月10日2.3.4版本主要是編寫和維護一些新特性,以及BUG錯誤修復。2006年3月11日發佈的2.3.5版本,因為發現嚴重錯誤而被撤回 [9]  ,在後續的2.3.6版本中修復。2.3.7時出了一個測試版本用於BUG修正和FreemarkerServlet的改進,其正式版中新增substring用於處理空的或缺失的變量 [10] 
模板引擎的執行流程 模板引擎的執行流程
2006年7月9日發佈2.3.8版本,提高了對JSP 2.0的兼容性 [11] 
2007年1月23日發佈2.3.9版本,包含了對JDK 1.5枚舉和通過BeansWrapper公共類字段的支持 [12] 
2007年4月20的2.3.10版本到2009年12月10日的2.3.16版本都是一些小性能改進和BUG修復。
2011年5月17日,FreeMarker 2.3.17 發佈,該版本主要進行了安全性的修復並擴充了一些內建函數 [13] 
2011年5月22日,FreeMarker 2.3.18 發佈,修復jar包相關的bug [14] 
2012年2月29日,FreeMarker 2.3.19 發佈,該版本修復了兩個重要的bug,另外新增對JSON字符串進行處理的方法json_string等小改動 [15] 
2013年6月27日,FreeMarker 2.3.20 發佈,主要對於使用IDE工具的修改 [16] 
2014年10月12日,FreeMarker 2.3.21 發佈,對Java版本的最低要求從1.2變為1.4。由於舊的BSD風格許可不被OSI所承認,且Visigoth Software Society停滯不前,其許可變更為Apache 2.0版,所有者轉為Attila Szegedi、Daniel Dekany和Jonathan Revusky(FreeMarker 2的主要開發者) [2] 
2015年3月1日,FreeMarker 2.3.22 發佈,在FTL模板和Java方面做了一些更改 [17] 
2015年7月1日,FreeMarker經過投票進入了Apache Incubator,其項目授予給Apache軟件基金會 [2] 
2015年7月5日,FreeMarker 2.3.23 發佈,在FTL模板和Java上做了大量修改。尤其增加了list中items和else的字指令,使常見遍歷任務更簡單 [18] 
2015年9月2日,FreeMarker的主代碼庫從GitHub導入到Apache軟件基金會的基礎設施中發展 [2] 
2018年3月21日,FreeMarker在Apache Incubator中升級為頂級項目 [2] 

freemarker工作原理

假設在一個應用系統中需要一個HTML頁面如下:
<html>
    <head>
        <title>Welcome!</title>
    </head>
    <body>
        <h1>Welcome Big Joe!</h1>
        <p>Our latest product:
        <a href="products/greenmouse.html">green mouse</a>!
    </body>
</html>
頁面中的用户名(即上面的“Big Joe”)是登錄這個網頁的訪問者的名字, 並且最新產品的數據應該來自於數據庫才能隨時更新。所以,不能直接在HTML頁面中輸入“Big Joe”、“greenmouse”及鏈接, 不能使用靜態HTML代碼。可以使用要求輸出的模板來解決,模板和靜態頁面是相同的,只是它會包含一些FreeMarker將它們變成動態內容的指令:
<html>
    <head>
        <title>Welcome!</title>
    </head>
    <body>
        <h1>Welcome ${user}!</h1>
        <p>Our latest product:
        <a href="${latestProduct.url}">${latestProduct.name}</a>!
    </body>
</html>
模板文件存放在Web服務器上,當有人來訪問這個頁面,FreeMarker就會介入執行,然後動態轉換模板,用最新的數據內容替換模板中${...}的部分,之後將結果發送到訪問者的Web瀏覽器中。訪問者的Web瀏覽器就會接收到例如第一個HTML示例那樣的內容(也就是沒有FreeMarker指令的HTML代碼),訪問者也不會察覺到服務器端使用的FreeMarker。(存儲在Web服務器端的模板文件是不會被修改的;替換也僅僅出現在Web服務器的響應中。)
為模板準備的數據整體被稱作為數據模型。數據模型是樹形結構(就像硬盤上的文件夾和文件),在視覺效果上, 數據模型可以是(這只是一個形象化顯示,數據模型不是文本格式,它來自於Java對象):
(root)
  |
  +- user = "Big Joe"
  |
  +- latestProduct
      |
      +- url = "products/greenmouse.html"
      |
      +- name = "green mouse"

早期版本中,可以從數據模型中選取這些值,使用user和latestProduct.name表達式即可。類比於硬盤的樹形結構,數據模型就像一個文件系統,“(root)”和latestProduct就對應着目錄(文件夾),而user、url和name就是這些目錄中的文件。
總體上,模板和數據模型是FreeMarker來生成輸出所必須的組成部分:模板 + 數據模型 = 輸出 [19] 

freemarker基本語法

  • ${...}:FreeMarker將會輸出真實的值來替換大括號內的表達式,這樣的表達式被稱為interpolation(插值)。
  • 註釋:註釋和HTML的註釋也很相似,但是它們使用<#-- and -->來標識。不像HTML註釋那樣,FTL註釋不會出現在輸出中(不出現在訪問者的頁面中),因為FreeMarker會跳過它們。
  • FTL標籤(FreeMarker模板的語言標籤):FTL標籤和HTML標籤有一些相似之處,但是它們是FreeMarker的指令,是不會在輸出中打印的。這些標籤的名字以#開頭。(用户自定義的FTL標籤則需要使用@來代替#) [20] 

freemarker指令

<#if condition>
    ...
<#elseif condition2>
    ...
<#elseif condition3>
    ...
<#else>
    ...
</#if>
if、elseif和else指令可以用來條件判斷是否越過模板的一個部分。condition必須計算成布爾值,否則錯誤將會中止模板處理。elseif和else必須出現在if內部(也就是在if的開始標籤和結束標籤之間)。if中可以包含任意數量的elseif(包括0個),而結束時else也是可選的 [21] 
假設 users 包含['Joe', 'Kate', 'Fred'] 序列:
<#list users as user>
    <p>${user}
</#list>

輸出:
    <p>Joe
    <p>Kate
    <p>Fred
list指令執行在list開始標籤和list結束標籤(list中間的部分)之間的代碼,對於在序列(或集合)中每個值指定為它的第一個參數。對於每次迭代,循環變量將會存儲當前項的值。循環變量僅僅存在於list標籤體內。而且從循環中調用的/函數不會看到它(就像它只是局部變量一樣)。<#list>與<#else>、<#sep>組合是可選的,而且僅從FreeMarker 2.3.23版本開始支持 [22] 
將版權信息單獨存放在頁面文件 copyright_footer.html 中:
<hr>
<i>
    Copyright (c) 2000 <a href="http://www.baidu.com">Baidu Inc</a>,
    <br>
    All Rights Reserved.
</i>

當需要用到這個文件時,可以使用 include 指令來插入:
<html>
    <head>
        <title>Test page</title>
    </head>
    <body>
        <h1>Test page</h1>
        <p>Blah blah...
        <#include "/copyright_footer.html">
    </body>
</html>
include可以在模板中插入另外一個FreeMarker模板文件(由路徑參數指定)。被包含模板的輸出格式是在include標籤出現的位置插入的。被包含的文件和包含它的模板共享變量,就像是被複制粘貼進去的一樣。include指令不能由被包含文件的內容所替代,它只是當FreeMarker每次在模板處理期間到達include指令時處理被包含的文件。所以對於如果include在list循環之中的例子,可以為每個循環週期內指定不同的文件名 [23] 

freemarker內建函數

常用處理 常用處理
內建函數很像子變量(也像Java中的方法),它們並不是數據模型中的東西,是FreeMarker在數值上添加的。為了清晰子變量是哪部分,使用?(問號)代替,.(點)來訪問它們。常用內建函數的示例:
  • user?html給出user的HTML轉義版本,比如&會由&來代替。
  • user?upper_case給出user值的大寫版本(比如“JOHN DOE”來替代“John Doe”)
  • animal.name?cap_first給出animal.name的首字母大寫版本(比如“Mouse”來替代“mouse”)
  • user?length給出user值中字符的數量(對於“John Doe”來説就是8)
  • animals?size給出animals序列中項目的個數
  • 如果在<#list animals as animal>和對應的標籤中:
    • animal?index給出了在animals中基於0開始的animal的索引值
    • animal?counter也像index,但是給出的是基於1的索引值
    • animal?item_parity基於當前計數的奇偶性,給出字符串“odd”或“even”。在給不同行着色時非常有用,比如在中。
一些內建函數需要參數來指定行為,比如:
  • animal.protected?string("Y", "N")基於animal.protected的布爾值來返回字符串“Y”或“N”。
  • animal?item_cycle('lightRow','darkRow')是item_parity更為常用的變體形式。
  • fruits?join(", ")通過連接所有項,將列表轉換為字符串,在每個項之間插入參數分隔符(比如“orange,banana”)
  • user?starts_with("J")根據user的首字母是否是“J”返回布爾值true或false。
內建函數應用可以鏈式操作,比如user?upper_case?html會先轉換用户名到大寫形式,之後再進行HTML轉義,和鏈式使用.(點)一樣 [20] 

freemarker空變量

數據模型中經常會有可選的變量(有時並不存在)。除了一些人為原因導致失誤外,FreeMarker不能引用不存在的變量,除非明確地告訴它當變量不存在時如何處理,如下兩種典型的處理方法:
這部分對程序員而言:一個不存在的變量和一個是null值的變量,對於FreeMarker來説是一樣的,所以這裏所指的“丟失”包含這兩種情況。
不論在哪裏引用變量,都可以指定一個默認值來避免變量丟失這種情況,通過在變量名後面跟着一個 !(感嘆號)和默認值。像下面的這個例子,當user不存在於數據模型時,模板將會將user的值表示為字符串 “visitor”。(當 user 存在時,模板就會表現出 ${user} 的值):
<h1>Welcome ${user!"visitor"}!</h1>
也可以在變量名後面通過放置??來詢問一個變量是否存在。將它和if指令合併,那麼如果user變量不存在的話將會忽略整個問候的代碼段:
<#if user??>
    <h1>Welcome ${user}!</h1>
</#if>
關於多級訪問的變量,比如 animals.python.price,書寫代碼:animals.python.price!0當且僅當animals.python永遠存在,而僅僅最後一個子變量price可能不存在時是正確的(這種情況下假設價格是0)。如果 animals或python不存在,那麼模板處理過程將會以“未定義的變量”錯誤而停止。為了防止這種情況的發生, 可以如下這樣來編寫代碼 (animals.python.price)!0。這種情況就是説animals或python不存在時,表達式的結果是 0。對於??也是同樣用來的處理這種邏輯的;將animals.python.price??對比(animals.python.price)??來看 [20] 

freemarker性能特點

模板並沒有包含程序邏輯來查找當前的訪問者是誰,或者去查詢數據庫獲取最新的產品。顯示的數據是在FreeMarker之外準備的,通常是一些“真正的”編程語言(比如Java)所編寫的代碼。模板作者無需知道這些值是如何計算出的。事實上,這些值的計算方式可以完全被修改,而模板可以保持不變,而且頁面的樣式也可以完全被修改而無需改動模板。當模板作者(設計師)和程序員不是同一人時,顯示邏輯和業務邏輯相分離的做法是非常有用的,即便模板作者和程序員是一個人,這麼來做也會幫助管理應用程序的複雜性。保證模板專注於顯示問題(視覺設計,佈局和格式化)是高效使用模板引擎的關鍵 [19] 
MyEclipse工具下的編輯界面 MyEclipse工具下的編輯界面
1. 通用性
能夠生成各種文本:HTML、XML、RTF、Java源代碼等等。
易於嵌入到產品中:輕量級;不需要Servlet環境。
插件式模板載入器:可以從任何源載入模板,如本地文件、數據庫等等。
可以按所需生成文本:保存到本地文件;作為Email發送;從Web應用程序發送它返回給Web瀏覽器。
2. 模板語言
所有常用的指令:include、if/elseif/else、循環結構
在模板中創建和改變變量。
幾乎在任何地方都可以使用複雜表達式來指定值。
命名的宏,可以具有位置參數和嵌套內容。
名字空間有助於建立和維護可重用的宏庫,或者將一個大工程分成模塊,而不必擔心名字衝突。
輸出轉換塊:在嵌套模板片段生成輸出時,轉換HTML轉義、壓縮、語法高亮等等;可以定義自己的轉換。
3. 通用數據模型
FreeMarker不是直接反射到Java對象,Java對象通過插件式對象封裝,以變量方式在模板中顯示。
可以使用抽象(接口)方式表示對象(JavaBean、XML文檔、SQL查詢結果集等等),告訴模板開發者使用。方法,使其不受技術細節的打擾。
xml配置 xml配置
4. 為Web準備
在模板語言中內建處理典型Web相關任務(如HTML轉義)的結構。
能夠集成到Model2 Web應用框架中作為JSP的替代。
支持JSP標記庫。
為MVC模式設計:分離可視化設計和應用程序邏輯;分離頁面設計員和程序員。
5. 智能的國際化和本地化
字符集智能化(內部使用UNICODE)。
數字格式本地化敏感。
日期和時間格式本地化敏感。
非US字符集可以用作標識(如變量名)。
多種不同語言的相同模板。
6. XML處理能力
<#recurse> 和<#visit>指令(2.3版本)用於遞歸遍歷XML樹。
在模板中清楚和直接的訪問XML對象模型 [24] 

freemarker評價

在所有采用網頁靜態化手段的網站中,FreeMarker使用的比例大大的超過了其他的一些技術。HTML靜態化也是某些緩存策略使用的手段,對於系統中頻繁使用數據庫查詢但是內容更新很小的應用,可以使用FreeMarker將HTML靜態化。比如一些網站的公用設置信息,這些信息基本都是可以通過後台來管理並存儲在數據庫中,這些信息其實會大量的被前台程序調用,每一次調用都會去查詢一次數據庫,但是這些信息的更新頻率又會很小,因此也可以考慮將這部分內容進行後台更新的時候進行靜態化,這樣就避免了大量的數據庫訪問請求,從而也就提高了網站的性能 [25] 
JSP相比,FreeMarker的一個優點在於不能輕易突破模板語言開始編寫Java代碼,因此降低了領域邏輯漏進視圖層的危險幾率。但缺點是需要一點附加配置來將其平穩地集成到應用程序中,一些IDE(集成開發環境)可能並不完全支持它,當然還有開發者或設計者也許需要學習一門陌生的模板語言。相關的JAR文件將要添加到WEB-INF/lib(在需要的時候,它們包含在Spring中) [26] 
參考資料
展開全部 收起