天天看點

Freemarker常用技巧(二)

1 list、break指令

<#list sequence as item>

  ...

</#list>

tem_index:目前變量的索引值.

item_has_next:是否存在下一個對象.

<#list ["星期一","星期二","星期三","星期四","星期五","星期六"] as x>

${x_index + 1}.${x}

<#if x_has_next>,</#if>

<#if x="星期四"><#break></#if>

輸出結果:

1.星期一,

2.星期二,

3.星期三,

4.興趣四,

2 import指令

<#import path as mapObject>

path:指定要被導入的模闆檔案.

mapObject:是一個Map對象.

意思:将path路徑中的變量都放在mapObject中.

例子:<#import "/lib/common.ftl" as com>

3 宏的基本用法

例如:

<#macro greet>

<font size="+2"> Hello JOE!</font>

</#macro>

使用時:

<@greet></@greet>

如果沒有體内容也可以用

<@greet />

可以在宏定義之後定義參數,宏參數是局部變量,隻在宏定義中有效。如:

<#macro greet person>

<font size="+2"> Hello ${person}!</font>

<@greet person="emma"> and <@greet person="LEO">

輸出為:

<font size="+2"> Hello emma!</font>

<font size="+2"> Hello LEO!</font>

注意:宏的參數是FTL表達式,是以,person=emma和上面的例子中具有不同的意義,這意味着将變量emma的值傳給person,這個值可能是任意一種資料類型,甚至是一個複雜的表達式。

宏可以有多個參數,使用時參數的次序是無關的,但是隻能使用宏中定義的參數,并且對所有參數指派。如:

<#macro greet person color>

<font size="+2" color="${color}"> Hello ${person}!</font>

<@greet color="black" person="emma" />正确

<@greet person="emma" />錯誤,color沒有指派,此時,如果在定義宏時為color定義預設值<#macro greet person color="black">這樣的話,這個使用方法就是正确的。

FreeMarker緩存處理

上周我面試了一個人,無意中我問了一個問題:freemarker加載模闆檔案的緩存政策是什麼呢?很遺憾,面試者沒有回答出來。後來,我告訴面試者,學習一個架構,不僅僅是要明白這個架構如何使用,還要了解一下架構的底層實作。本文主要是給大家說一下FreeMarker的緩存實作部分。本文的内容大部分來自于網際網路,要想更深入的了解與掌握

FreeMarker緩存處理,我個人覺得還是要深入研讀源碼,然後再看看網上的分析以加深印象,這樣的學習效果是最好的。

FreeMarker 的緩存處理主要用于模版檔案的緩存,一般來講,模版檔案改動不會很頻繁,在一個流量非常大的網站中,如果頻繁的讀取模版檔案對系統的負擔還是很重的,是以 FreeMarker 通過将模版檔案的内容進行緩存,來降低模版檔案讀取的頻次,降低系統的負載。

當處理某個模版時,FreeMarker直接從緩存中傳回對應的 Template 對象,并有一個預設的機制來保證該模版對象是跟模版檔案同步的。如果使用的時候 FreemarkerServlet 時,有一個配置項template_update_delay用來指定更新模版檔案的間隔時間,相當于多長時間檢測一下是否有必要重新加載模版檔案,0 表示每次都重新加載,否則為多少毫秒鐘檢測一下模版是否更改。

FreeMarker定義了一個統一的緩存處理接口CacheStorage,預設的實作是 MruCacheStorage 最近最少使用的緩存政策。一般情況下,很少需要對緩存進行擴充處理。您可以通過下面的代碼指定最大緩存的模版數:

1

cfg.setCacheStorage(new freemarker.cache.MruCacheStorage(20, 250))

其中第一個參數是最大的強引用對象數,第二個為最大的弱引用對象數。這兩個值FreeMarker預設的是0和 Integer.MAX_VALUE,表明模版緩存數是無限的。

freemarker空值的處理

FreeMarker的變量必須指派,否則就會抛出異常。而對于FreeMarker來說,null值和不存在的變量是完全一樣的,因為FreeMarker無法了解null值。FreeMarker提供兩個運算符來避免空值:

(1)!運算符:指定缺失變量的預設值;

(2)??運算符:判斷變量是否存在。

!運算符有兩種用法:variable!或variable!defaultValue。第一種用法不給變量指定預設值,表明預設值是空字元串、長度為0的集合、或長度為0的Map對象。

注意:使用!運算符指定預設值并不要求預設值的類型和變量類型相同。下面是一個小例子:

<#-- ${sss}沒有定義這個變量,會報異常!-->

${sss!} <#--沒有定義這個變量,預設值是空字元串!-->

${sss!"abc"} <#--沒有定義這個變量,預設值是字元串abc!-->

??運算符傳回布爾值,如:variable??,如果變量存在,傳回true,否則傳回false。一般情況下與if指令共同使用。将它和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)??來看。

freemarker資料類型

freemarker的資料類型主要包括下面幾類:

字元串類型

定義字元串可以使用雙引号和單引号,例如:

<#assign temp = "some text"  />

或者

<#assign temp = 'some text'  />

這兩種形式是相等的。字元串中可以使用轉義字元"\"。如果字元串内有大量的特殊字元,則可以在引号的前面加上一個字母r,則字元串内的所有字元都将直接輸出。例如:"It's \"quoted\"" 或者 r"C:\raw\string"

數字類型

輸入不帶引号的數字就可以直接指定一個數字,必須使用點作為小數的分隔符而不能是其他的分組分隔符。可以使用-或+來表明符号(+是多餘的)。科學記數法暫不支援使用(1E3就是錯誤的),而且也不能在小數點之前不寫0(.5也是錯誤的)。

哈希表類型

鍵和值成對出現并以冒号分隔,最外面使用花括号。看這個例子:

<#assign temp = {"name":"green mouse", "price":150} />

注意到名字和值都是表達式,但是用來檢索的名字就必須是字元串類型的。

序列類型

指定一個序列,使用逗号來分隔其中的每個子變量,然後把整個清單放到方括号中。例如:

<#assign nums=[1,2,3,4,5,77,8,99]/>

使用list指令将序列輸出,如下所示:

<#list nums as num>

   ${num}

還可以采用數字範圍定義了一個連續的序列,例如:

<#assign nums=12..99/>

這種方式定義的序列的内容是12到99。總之,使用數字範圍也可以表示一個數字集合,如1..5等同于集合[1,2, 3, 4, 5];同樣也可以用5..1來表示[5, 4, 3, 2, 1]。

時間類型

FreeMarker支援date、time、datetime三種類型,這三種類型的值無法直接指定,通常需要借助字元串的date、time、datetime三個内建函數進行轉換才可以:

 <#assign test1 = "2009-01-22"?date("yyyy-MM-dd") />;

 <#assign test2 ="16:34:43"?time("HH:mm:ss") />

 <#assign test2 = "2009-01-22 17:23:45"?datetime("yyyy-MM-dd HH:mm:ss") />

布爾類型

直接使用true或false,不使用引号。例如:<#assign temp = true />

freemarker的配置簡介

在freemarker啟動的過程中,參與配置功能的類主要有四個:Configurable,Configuration,Template和Environment。下面給大家簡單介紹一下這裡類的特點,内容主要是出自freemarker的源碼,而且這些英文比較簡單,稍微耐心一點就可以讀懂的。

This is a common superclass of {@link freemarker.template.Configuration},{@link freemarker.template.Template}, and {@link Environment} classes.

It provides settings that are common to each of them. FreeMarker uses a three-level setting hierarchy - the return value of every setting getter method on <code>Configurable</code> objects inherits its value from its parent <code>Configurable</code> object, unless explicitly overridden by a call to a corresponding setter method on the object itself. The parent of an 

<code>Environment</code> object is a <code>Template</code> object, the parent of a <code>Template</code> object is a <code>Configuration</code> object.

The main entry point into the FreeMarker API; encapsulates the configuration settings of FreeMarker, also serves as a central template-loading and caching service.

This class is meant to be used in a singleton pattern. That is, you create an instance of this at the beginning of the application life-cycle, set its {@link #setSetting(String, String) configuration settings} there (either with the setter methods like {@link #setTemplateLoader(TemplateLoader)} or by loading a {@code .properties} file), and then use that single instance everywhere in your application. Frequently re-creating {@link Configuration} is a typical and grave mistake from performance standpoint, as the {@link Configuration} holds the template cache, and often also the class introspection cache, which then will be lost. (Note that, naturally,having multiple long-lived instances,like one per component that internally uses FreeMarker is fine.)  

The basic usage pattern is like:

// Where the application is initialized; in general you do this ONLY ONCE in the application life-cycle!

Configuration cfg = new Configuration(VERSION_X_Y_Z));

// Where X, Y, Z enables the not-100%-backward-compatible fixes introduced in

// FreeMarker version X.Y.Z  and earlier (see {@link #Configuration(Version)}).

cfg.setSomeSetting(...);

cfg.setOtherSetting(...);

Stores an already parsed template, ready to be processed (rendered) for unlimited times, possibly from multiple threads.

Typically, you will use {@link Configuration#getTemplate(String)} to create/get {@link Template} objects, so you don't construct them directly. But you can also construct a template from a {@link Reader} or a {@link String} that contains the template source code. But then it's important to know that while the resulting {@link Template} is efficient for later processing, creating a new {@link Template} itself is relatively expensive. So try to re-use {@link Template} objects if possible.{@link Configuration#getTemplate(String)} does that (caching {@link Template}-s) for you, but the constructor of course doesn't, so it's up to you to solve then.

Objects of this class meant to be handled as immutable and thus thread-safe. However, it has some setter methods for changing FreeMarker settings. Those must not be used while the template is being processed, or if the template object is already accessible from multiple threads.

Object that represents the runtime environment during template processing. For every invocation of a <tt>Template.process()</tt> method, a new instance

of this object is created, and then discarded when <tt>process()</tt> returns.

This object stores the set of temporary variables created by the template,the value of settings set by the template, the reference to the data model root,etc. Everything that is needed to fulfill the template processing job.

Data models that need to access the Environment object that represents the template processing on the current thread can use the {@link #getCurrentEnvironment()} method.

If you need to modify or read this object before or after the process call, use {@link Template#createProcessingEnvironment(Object rootMap, Writer out, ObjectWrapper wrapper)}