概念
FreeMarker是一個模板引擎,一個基於模板生成文本輸出的通用工具,使用純Java編寫。它是為Java程序員提供的一個開發包。它不是面向最終用戶的,而是為程序員提供的一款可以嵌入他們所開發產品的應用程序。
介紹
那么,FreeMarker是一款怎樣的工具呢?FreeMarker實際上是被設計用來生成HTML Web頁面,尤其是通過實現了基於MVC模式的Java Servlet應用程序。使用MVC模式的動態頁面的設計構思使得你可以將前端設計師從程序員中分離出來。所有人各司其職,發揮其最擅長的一面。
網頁設計師可以改寫頁面的顯示效果而不受程序員編譯代碼的影響,因為應該程序的邏輯和頁面設計已經被分開了。頁面模板代碼不會收到復雜程序代碼的影響。這種分離的思想即便對一個程序員和頁面設計師是同一個人的項目來說也都是非常有用的,因為分離使得代碼保持簡潔而且易於維護。
盡管FreeMarker也擁有一些編程能力,但通常由Java程序准備要顯示的數據,由FreeMarker生成頁面,通過模板顯示准備的數據(如下圖)。

FreeMarker不是一個Web應用框架,而適合作為Web應用框架的一個組件,但是FreeMarker引擎本身並不知道HTTP協議或Java Servlet的存在。它僅僅來生成文本內容。既然是這樣,它也非常適用於非Web應用程序的開發環境。知識要注意的是,我們使用FreeMarker作為視圖層的組件,是為了給諸如Struts這樣的Model2應用框架提供現成的解決方案,你也可以在模板中使用JSP標記庫。
特性
- 通用目標
易於嵌入到你的產品中,輕量級,不需要Servlet環境
能夠生成各種文本:HTML、XML、RTF、Java源代碼等等
插件式模板載入器,可以從任何源載入模板,如本地文件、數據庫等等
你可以按自己所需生成文本,保存到本地文件,作為Email發送,從Web應用程序發送它返回給Web瀏覽器
- 強大的模板語言
在模板中創建和改變變量
命名的宏,可以具有位置參數和嵌套內容
幾乎在任何地方都可以使用復雜表達式來指定值
所有常用的指令,include、if/elseif/else、循環結構
名字空間有助於建立和維護可重用的宏庫,或者將一個大工程分成模塊,而不必擔心名字沖突
輸出轉換塊,在嵌套模板片段生成輸出時,轉換HTML轉義、壓縮、語法高亮等等,你可以定義自己的轉換
- 通用數據模型
FreeMarker不是直接反射到Java對象,Java對象通過插件式對象封裝,以變量方式在模板中顯示
你可以使用抽象(接口)方式表示對象(JavaBean、XML文檔、SQL查詢結果集等),告訴模板開發者使用方法,使其不受技術細節的打擾
- 為Web准備
支持JSP標記庫
能夠集成到Model2 Web應用框架中作為JSP的替代
在模板語言中內建處理典型Web相關任務(如HTML轉義)的結構
為MVC模式設計,分離可視化設計和應用程序邏輯,分離頁面設計師和程序員
- 智能的國際化和本地化
數字格式本地化敏感
多種不同語言的相同模板
日期和時間格式本地化敏感
字符集智能化(內部使用UNICODE)
非US字符集可以用作標識(如變量名)
- 強大的XML處理能力
在模板中清楚和直覺的訪問XML對象模型
<#recurse>和<#visit>指令用於遞歸遍歷XML樹
優勢
- 可以徹底的分離表現層和業務邏輯
使用JSP開發過程中,在頁面中大量的存在業務邏輯代碼,使得頁面的內容非常混亂,在后期大量的修改維護過程中就變得非常的困難。
FreeMarker不支持Java腳本代碼,FreeMarker的原理是,模板+數據模型=輸出。模板只負責數據在頁面中的表現,不涉及任何的邏輯代碼,而所有的邏輯都是由數據模型來處理的。用戶最終看到的輸出是模板和數據模型合並后創建的。
- 可以提高開發效率
在以往的開發中,使用的都是JSP頁面來展示數據的,即所謂的表現層。我們都知道,JSP在第一次執行的時候需要轉換成Servlet類,開發階段進行功能調試時,需要頻繁的修改JSP,每次修改都要編譯和轉換,那么試想一下,一天中我們浪費在程序編譯的時間有多少。
相對於JSP來說,FreeMarker模板技術不存在編譯和轉換的問題,所以就不會存在上述問題。而且開發過程中,我們再不必等待界面設計開發人員完成頁面原型后,我們再來開發程序。
而且,一些特定的系統,比如OA工作流系統中,就需要動態生成表單技術,這就為其提供了很好的實現依據。使得在整個流程的進行中,生成不同的表單就簡單了很多。
- 使得開發過程中的人員分工更加明確
以往用JSP顯示數據時,一些程序員並不熟悉界面設計技術,反之界面開發人員,也並不熟悉程序語言。對兩者而言,交替性的工作本身就有難度。有時候稍有不慎,可能會將整個頁面元素刪除或去掉了某個程序符號,使得頁面走樣或程序錯誤,這樣就需要雙方相互溝通協作,解決出現的問題。有時候因為項目中的時間、任務量等因素的存在,可能這個工作就由一個人來完成,這樣就可能加大某一方開發人員的工作量。
使用FreeMarker后,作為界面開發人員,只專心創建HTML文件、圖像以及Web頁面的其他可視化方面,不用理會數據;而程序開發人員則專注於系統實現,負責為頁面准備要顯示的數據。
不足
- 在修改模板后,可能會看到已經過期的數據
使用FreeMarker模板技術,生成靜態的HTML頁面后,如果一旦模板改變,而沒有及時更新模板生成的HTML頁面的話,用戶看到的就是過期的數據。
- FreeMarker的變量必須有值
FreeMarker模板技術在應用過程中,FreeMarker中的變量必須要賦值,如果不賦值,那么就會拋出異常。FreeMarker沒有一個默認的null處理,甚至也不接受一個null值。想避免錯誤就要應用if/elseif/else 指令進行判段,如果對每一個變量都判斷的話,那么則反而增加了編程的麻煩。
- FreeMarker的Map限定Key必須是String,其他數據類型無法操作
Map問題,即FreeMarker中不能支持非String的Key值,這樣在進行一些復雜迭代時就需要作一些其他的轉換,如將一個Map拆分為兩個或多個Map。
- FreeMarker不支持集群應用
為了編成的方便性,把序列化的東西都放到了Session中,如Session,request等,在開發的過程中確實方便,但如果將應用放到集群中,就會出現錯誤。
一個小Demo
首先,需要下載FreeMarker的jar包,這里提供一個下載鏈接:
freemarker.jar
然后,將這個freemarker.jar放到Web項目的 WebRoot\WEB-INF\lib 目錄下
最后,我把自己寫的測試類貼出來,分享一下。
FreemarkerTest類 代碼
- <span style="font-family:Microsoft YaHei;">import java.io.BufferedWriter;
- import java.io.File;
- import java.io.FileWriter;
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.util.HashMap;
- import java.util.Map;
- import freemarker.template.Configuration;
- import freemarker.template.DefaultObjectWrapper;
- import freemarker.template.Template;
- import freemarker.template.TemplateException;
- import freemarker.template.TemplateExceptionHandler;
- import junit.framework.TestCase;
- public class FreemarkerTest extends TestCase {
- private String dir = "E:/.../OA/TestTotal/src/com/bjsxt/oa/freemarker";
- public void testFreemarker() {
- Configuration cfg = new Configuration();
- try {
- // 從哪里加載模板文件
- cfg.setDirectoryForTemplateLoading(new File(dir));
- // 定義模版的位置,從類路徑中,相對於FreemarkerManager所在的路徑加載模版
- // cfg.setTemplateLoader(new ClassTemplateLoader(FreemarkerManager.class, "templates"))
- // 設置對象包裝器
- cfg.setObjectWrapper(new DefaultObjectWrapper());
- // 設置異常處理器
- cfg
- .setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
- // 定義數據模型
- Map root = new HashMap();
- root.put("abc", "世界,你好");
- // 通過freemarker解釋模板,首先需要獲得Template對象
- Template template = cfg.getTemplate("test.ftl");
- // 定義模板解釋完成之后的輸出
- PrintWriter out = new PrintWriter(new BufferedWriter(
- new FileWriter(dir+"/out.txt")));
- try {
- // 解釋模板
- template.process(root, out);
- } catch (TemplateException e) {
- e.printStackTrace();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }</span>
下面是定義的模板 test.ftl
test.flt 代碼
- <span style="font-family:Microsoft YaHei;">第一個測試程序:${abc}</span>
最后運行的結果如下
輸出了out.txt文件,out.txt文件中的內容如下:
- <span style="font-family:Microsoft YaHei;">第一個測試程序:世界,你好</span>
