歡迎您光臨本站 註冊首頁

全面探索 FreeMarker 模版引擎的擴展性

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  
本文全面介紹了在 Java 語言中功能十分強大的模版引擎 — FreeMarker,以及對 FreeMarker 的可擴展性進行了全面探索。

FreeMarker 模版引擎簡介

FreeMarker 是一個採用 Java 開發的模版引擎,是一個基於模版生成文本的通用工具。 FreeMarker 被設計用來生成 HTML Web 頁面,特別是基於 MVC 模式的應用程序。雖然 FreeMarker 具有一些編程的能力,但通常由 Java 程序準備要顯示的數據,由 FreeMarker 生成頁面,並通過模板顯示準備的數據(如下圖)。


圖 1. FreeMarker 工作原理

FreeMarker 非常簡單,只需要一個 Freemarker.jar 文件(無需任何配置文件)即可包含所有的功能。但 FreeMarker 的功能卻是非常的強大,相比較另外一個非常著名的 Java 模版引擎 —— Velocity 來說,FreeMarker 的功能讓您驚嘆,但其學習的曲線也較 Velocity 要長很多。

本文主要介紹如何利用 FreeMarker 強大的可擴展性來輸出各種文本信息,這不是 FreeMarker 的入門學習材料,如果您尚未對 FreeMarker 有所了解,或者還沒有使用過 FreeMarker 的話,那不妨先上手后再來閱讀本文。

FreeMarker 主要提供了如下幾個方面的擴展性功能:

  1. 自定義宏
  2. 自定義函數
  3. 自定義模版文件載入器
  4. 緩存處理
  5. 異常處理

FreeMarker 自定義宏

FreeMarker 和 Velocity 都提供可自定義宏的功能,但 FreeMarker 的宏功能更加強大,包括允許通過名稱和參數的位置進行參數傳遞;允許設置參數的默認值;支持宏的嵌套;宏可以先使用再聲明;支持命名空間等。

下面我們針對這些功能給出一個簡單但是完整的演示例子,先看看代碼:


清單 1. 宏定義文件 ( html.ftl )
				  <#macro html title charset="utf-8" lang="zh-CN">    <html>    <head>     <meta http-equiv="Content-Type" content="text/html; charset=${charset}" />     <meta http-equiv="Content-Language" content="${lang}"/>     <title>${title}</title>    </head>    <body>       <#nested>    </body>    </html>    </#macro>

在這個宏定義文件中,我們聲明了一個名為 html 的宏,該宏是為了生成一個 HTML 頁面的框架。它具有三個參數分別是 title 、charset 和 lang ,其中 charset 和 lang 分別指定了默認的值。

再來看看如何調用該宏:


清單 2. 調用宏
				  <#include "html.ftl">    <@html title="FreeMarker 宏測試 ">   	歡迎使用 FreeMarker 模版引擎   </@html>

在 FreeMarker 中,用戶自定義的宏必須以 @ 開頭來調用,並傳入頁面標題 title 的參數。而 <@html> 標籤中包含的文本“歡迎使用 FreeMarker 模版引擎”將替換宏定義中的 <#nested> 標籤。因此這個模版將會生成如下的 HTML 信息:


清單 3. 模版生成結果
				  <html>    <head>     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />     <meta http-equiv="Content-Language" content="zh-CN"/>     <title>FreeMarker 宏測試 </title>    </head>    <body>   	歡迎使用 FreeMarker 模版引擎   </body>    </html>

而 Velocity 本身並不提供嵌套模版的功能,它必須依賴 Velocity-Tools 這個項目來實現。另外對於一些需要實現更複雜邏輯的宏,還可以通過 Java 類來進行定義。 FreeMarker 提供了一個 TemplateDirectiveModel 介面,通過實現該介面可以實現自定義宏的功能,這樣可以更好的跟應用邏輯進行集成,不過需要注意的是暫不支持通過參數的位置來調用宏,調用時必須指定參數名,該問題將在 FreeMarker 2.4 中得以解決。下面是一個簡單的例子:


清單 4. 自定義宏功能的例子
				  /**   * 將標籤中的代碼全部轉為大寫並輸出   * @author Winter Lau (javayou@gmail.com)   * 使用方法:   * <@upper>Welcome to http://www.oschina.net</@upper>   */  public class UpperDirective implements TemplateDirectiveModel {  	      public void execute(Environment env,              Map params, TemplateModel[] loopVars,              TemplateDirectiveBody body)              throws TemplateException, IOException {          // Check if no parameters were given:          if (!params.isEmpty()) {              throw new TemplateModelException(                      "This directive doesn't allow parameters.");          }          if (loopVars.length != 0) {                  throw new TemplateModelException(                      "This directive doesn't allow loop variables.");          }                    // If there is non-empty nested content:          if (body != null) {              // Executes the nested body. Same as <#nested> in FTL, except              // that we use our own writer instead of the current output writer.              body.render(new UpperCaseFilterWriter(env.getOut()));          } else {              throw new RuntimeException("missing body");          }      }            /**       * A {@link Writer} that transforms the character stream to upper case       * and forwards it to another {@link Writer}.       */       private static class UpperCaseFilterWriter extends Writer {                   private final Writer out;                       UpperCaseFilterWriter (Writer out) {              this.out = out;          }            public void write(char[] cbuf, int off, int len)                  throws IOException {              char[] transformedCbuf = new char[len];              for (int i = 0; i < len; i++) {                  transformedCbuf[i] = Character.toUpperCase(cbuf[i + off]);              }              out.write(transformedCbuf);          }            public void flush() throws IOException {              out.flush();          }            public void close() throws IOException {              out.close();          }      }    }  

接下來我們需要重載 FreemarkerServlet ,植入該指令擴展,代碼如下:


清單 5. 重載 FreemarkerServlet
				  @Override  protected Configuration createConfiguration() {  	Configuration cfg = super.createConfiguration();  	cfg.setSharedVariable("upper", new UpperDirective());  	return cfg;  }  

在頁面模版中使用<@upper>Welcome to http://www.oschina.net</@upper>試試吧。

FreeMarker 自定義函數

與宏不同,宏一般用來執行某個過程,而函數可以定義返回值,例如對一組數據求和、平均值、最大值、最小值等等運算。 FreeMarker 的函數支持可變個數的參數。例如下面定義了一個求平均值的函數:


清單 6. 求平均值的函數例子
				  <#function avg nums...>     <#local sum = 0>     <#list nums as num>       <#local sum = sum + num>     </#list>     <#if nums?size != 0>       <#return sum / nums?size>     </#if>    </#function>

其中函數名為 avg ,支持可變個數的參數 nums 。可用下面的代碼來要調用該函數:

${avg(3,5,100,3453)}

跟宏相同,FreeMarker 也可以用 Java 來編寫自定義函數。例如我們用 Java 代碼來生成一個隨機的整數,其代碼如下:


清單 7. 使用 Java 編寫的自定義函數
				  /**   * 生成一個隨機的整數   * @author Winter Lau (javayou@gmail.com)   * @url http://www.oschina.net   */  public class RandomFunction implements TemplateMethodModel {    	final static Random rnd_seed = new Random(System.currentTimeMillis());  	  	/* (non-Javadoc)  	 * @see freemarker.template.TemplateMethodModel#exec(java.util.List)  	 */  	@SuppressWarnings("unchecked")  	public Object exec(List args) throws TemplateModelException {  		return rnd_seed.nextInt(Integer.parseInt((String)args.get(0)));  	}    }  

同樣的,需要將該函數的定義植入 FreeMarker :

cfg.setSharedVariable("rand",newRandomFunction());


[火星人 ] 全面探索 FreeMarker 模版引擎的擴展性已經有426次圍觀

http://coctec.com/docs/linux/show-post-68847.html