在B/S程式設計中,經常有美工和程序員二個角色,他們具備不一樣專業技能:美工專一於表現——建立頁面、風格、佈局、效果等等可視元素;而程序員則忙於建立程式的商業流程,生成設計頁面要顯示的數據等等。不少時候,要顯示的資料在設計的時候並不存在,它們通常是在運行時由程式產生的,好比執行「價格不高於800NT的USB Disk」查詢的返回結果。這種技術需求產生了JSP等Scriptlet,JSP十分強大,可是也經常被濫用,並致使一些不良的後果
將邏輯和表現混合在一塊兒。
破壞了美工和程序員職責的正常分解。
使JSP頁面難以閱讀和維護。
模板引擎就是爲了解決上面的問題而產生的。在設計HTML的時候,咱們加入一些特定指令來指定要插入哪些數據,這些加了特殊指令的HTML或者其餘文本,咱們稱爲模板(Template)。而模板引擎會在輸出頁面時,用適當的數據替代這些代碼。
模板和嵌入JSP的HTML是不一樣的,模板指令只有頗有限的編程能力,能夠避免混入商業邏輯。
三萬英尺俯瞰FreeMarker
簡單的說,FreeMarker就是一種用Java編寫的模板引擎,它根據模板輸出多種規格的文本。特別指出的是,FreeMarker與Web應用框架無關,它一樣能夠應用在非Web應用程序環境中。
下面咱們來看看FreeMarker的模板:(product.ftl)
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Welcome ${user}!</h1>
<p>Our latest product:
<a href="${latestProduct.url}"
>${latestProduct.name}</a>!
</body>
</html>
這個例子是在簡單的HTML中加入了一些由${…}包圍的特定代碼,這些特定代碼就是FreeMarker的指令。
至於user、latestProduct.url和latestProduct.name的具體內容則來自於數據模型(data model)。數據模型由程序員編程來建立,向模板提供變化的信息,這些信息來自於數據庫、文件,甚至於在程序中直接生成。
模板設計者不關心數據從那兒來,只知道使用已經創建的數據模型。
藉助FMPP(FreeMarker PreProcessor)來運行FreeMarker
首先說明的是FreeMarker的運行並不依賴於FMPP。FMPP只是一個FreeMarker的輔助工具,有了它,咱們能夠快速地調試FreeMarker輸出結果,而不須要藉助Java編程,這能夠大大地減輕美工設計人員的調試難度。
在磁盤上創建相關的文件夾:
D:/work/src/product.ftl
D:/work/out/
D:/work/data/product.tdd
D:/work/config.fmpp
咱們使用的配置文件(config.fmpp)設置以下:
sourceRoot: src
outputRoot: out
logFile: log.fmpp
modes: [
copy(common/**/*.*, resource/*.*)
execute(*.ftl)
ignore(templates/*.*, .project, **/*.xml, xml/*.*, *.js)
]
replaceExtensions: [ftl, html]
sourceEncoding: gb2312
data: tdd(../data/product.tdd)
注意:"data: tdd(../data/product.tdd)" 指定了模板的數據源,TDD是fmpp支持的數據格式之一 ,關於TDD介紹可參閱fmpp文檔,也可參看TDD 。product.tdd內容是這樣的:
{
user: "Big Joe"
latestProduct: {url: "products/greenmouse.html"
, name: "Green Mouse"
}
}
如今在dos下執行(假設FMPP安裝在D:/FMPP下):
D:/work/>D:/FMPP/bin/fmpp
最後的輸出結果是這樣的,存放在文件out/product.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>
正如FreeMarker文檔中所敘述的,FreeMarker的工做原理就是:
模板+數據=輸出!
FreeMarker並不侷限於生成html,甚至能夠產生java代碼,這僅僅取決於你如何設計模板而已。
如今有了FMPP這個強大工具,咱們接下來能夠快速學習FreeMarker的相關指令。let us go!
在FreeMarker模板中使用的三種基本對象類型:Scalars、Hashes 和Sequences。在解釋這些對象類型以前,咱們先來看看數據模型。
典型的數據模型是樹型結構,能夠有任意深的層次,好比說:
(root)
|
+- animals
| |
| +- mouse
| | |
| | +- size = "small"
| | |
| | +- price = 50
| |
| +- elephant
| | |
| | +- size = "large"
| | |
| | +- price = 5000
| |
| +- python
| |
| +- size = "medium"
| |
| +- price = 4999
這棵樹上的每一片葉子在FreeMarker中就稱爲Scalars,用來存儲單值。Scalars保存的值 有兩種類型:字符串(用引號括起,能夠是單引 號或雙引號)、數字(不要用引號將數字括起,這會做爲字符串處理)、日期和boolean值。對scalars的訪問要從root開始,各部分用「.」分 隔,如animals.mouse.price。
樹的每個分支關聯一個惟一的查詢名字,例如「mouse」,「elephant」,這些分支充當了其餘對象(size,price)的容器,這種結構則稱爲Hashes,參考Hashes的TDD定義 。
Sequences的做用與Hashes相似,也能夠充當其它對象的容器,只是不使用變量名字,而使用數字索引:
(root)
|
+- animals
| |
| +- (1st)
| | |
| | +- name = "mouse"
| | |
| | +- size = "small"
| | |
| | +- price = 50
| |
| +- (2nd)
| | |
| | +- name = "elephant"
| | |
| | +- size = "large"
| | |
| | +- price = 5000
| |
| +- (3rd)
| |
| +- name = "python"
| |
| +- size = "medium"
| |
| +- price = 4999
能夠經過animals[0].name來訪問相應的Scalars。參考Sequences的TDD定義
針對上面三種對象類型的操做,能夠參看對象類型的各類操做
模板與指令
除了相關的文本外,在FreeMarker模板中能夠包括下面三種特定部分:
${…}:稱爲插補(interpolations),FreeMarker會在輸出時用實際值進行替代。
指令:也叫FreeMarker標記,與HTML標記相似,但用#開始(有些以@開始 ,在後面敘述)。
註釋:包含在<#-- 和 -->(而不是<!--和-->)之間文本。
控制指令
if指令
if指令與大部分程式語言同樣,也支持<#else if..>,再也不贅述。
<#if
animals.python.price < animals.elephant.price>
Pythons are cheaper than elephants today.
<#else
>
Pythons are not cheaper than elephants today.
</#if
>
list指令
list指令將遍歷Sequences裏的每個元素。list指令有兩個隱含的特殊變量:
item_index 該變量將返回元素在Sequences裏的索引值。
item_has_next 該變量是boolean型,false代表該元素是Sequences裏的最後一個元素。
<p>We have these animals:
<table border=1>
<tr><th>Id<th>Name<th>Price
<#list animals as being>
<tr><td>${being_index+1}<td>${being.name}<td>${being.price} Euros
</#list>
</table>
上面的模板能夠依次列印出三種動物的名字和價格,being_index和being_has_next則是它的特殊變量。
能夠用<#break>指令提早結束list循環。
switch指令
與其餘語言的switch語句相似。
<#switch
being.size>
<#case
"small"
>
This will be processed if
it is small
<#break
>
<#case
"medium"
>
This will be processed if
it is medium
<#break
>
<#case
"large"
>
This will be processed if
it is large
<#break
>
<#default
>
This will be processed if
it is neither
</#switch
>
注意事項
FTL區分大小寫,因此list是正確的FTL指令,而List不是;${name}和${NAME}是不一樣的
Interpolation只能在文本中使用
FTL標記不能位於另外一個FTL標記內部。
註釋能夠位於FTL標記和Interpolation內部。
多餘的空白字符會在模板輸出時移除
可用[#if..]來替代<#if..>,避免於HTML標記混淆。 html