一、前言
Velocity作為歷史悠久的模板引擎不單單可以替代JSP作為Java Web的服務端網頁模板引擎,而且可以作為普通文本的模板引擎來增強服務端程序文本處理能力。而且Velocity被移植到不同的平臺上,如.Net的NVelocity和js的Velocity.js,雖然各平臺在使用和實現上略有差別,但大部分語法和引擎核心的實現是一致的,因此學習成本降低不少哦。
最好的學習資源——官網:http://velocity.apache.org/
本系列打算采用如下結構對Velocity進行較為全面的學習,若有不妥或欠缺望大家提出,謝謝。
2. VTL語法詳解
4. 基礎配置項
5. 深入模板引擎及調優配置
二、VTL語法詳解
VTL的語句分為4大類:注釋、直接輸出的內容、引用和指令。另外由于VTL中以 # 和 $ 作為關鍵字起始字符,因此輸出它們時需要通過轉義符 \ 來將其轉換為普通字符。
由于內容較多,特設目錄一坨!
六. 指令(#set、#if、#foreach、#include、#parse、#break、#stop、#macro、#define、#evaluate)
## 行注釋內容
#*
塊注釋內容1
塊注釋內容2
*#
#**
文檔注釋內容1
文檔注釋內容2
*#
踩過的坑
塊注釋和文檔注釋雖然均不輸出到最終結果上,但會導致最終結果出現一行空行。使用行注釋則不會出現此情況。
也就是不會被引擎解析的內容。
#[[
直接輸出的內容1
直接輸出的內容2
]]#
引用語句就是對引擎上下文對象中的屬性進行操作。語法方面分為常規語法( $屬性 )和正規語法( ${屬性} )。在普通模式下上述兩種寫法,當引擎上下文對象中沒有對應的屬性時,最終結果會直接輸出 $屬性 或 ${屬性} ,若要不輸出則需要改寫為 $!屬性 和 $!{屬性} 。
1. 變量(就是引擎上下文對象的屬性)
$變量名, 常規寫法,若上下文中沒有對應的變量,則輸入字符串"$變量名" ${變量名}, 常規寫法,若上下文中沒有對應的變量,則輸入字符串"${變量名}" $!變量名, 常規寫法,若上下文中沒有對應的變量,則輸入空字符串"" $!{變量名}, 常規寫法,若上下文中沒有對應的變量,則輸入空字符串""
變量的命名規則:
由字母、下劃線(_)、破折號(-)和數字組成,而且以字母開頭。
變量的數據類型為:
Integer、Long等簡單數據類型的裝箱類型;
String類型;
Object子類;
Object[] 數組類型,從1.6開始Velocity將數組類型視為 java.util.List 類型看待,因此模板中可調用 size() 、 get(int index) 和 isEmpty() 的變量方法;
java.util.Collection子類;
java.util.Map子類;
java.util.Iterator對象;
java.util.Enumeration對象。
$變量名.屬性, 常規寫法 ${變量名.屬性}, 正規寫法 $!變量名.屬性, 常規寫法 $!{變量名.屬性}, 正規寫法
屬性搜索規則:
Velocity采用一種十分靈活的方式搜索變量的屬性, 具體如下:
// 假如引用$var.prop,那么Velocity將對prop進行變形,然后在$var對象上嘗試調用 // 變形和嘗試的順序如下 1. $var.getprop() 2. $var.getProp() 3. $var.get("prop") 4. $var.isProp() // 對于$var.Prop則如下 1. $var.getProp() 2. $var.getprop() 3. $var.get("Prop") 4. $var.isProp()
因此獲取 java.util.Map 對象的鍵值時可以簡寫為 $map.key ,Velocity會自動轉為 $map.get("key") 來搜索!
$變量名.方法([入參1[, 入參2]*]?), 常規寫法 ${變量名.方法([入參1[, 入參2]*]?)}, 正規寫法 $!變量名.方法([入參1[, 入參2]*]?), 常規寫法 $!{變量名.方法([入參1[, 入參2]*]?)}, 正規寫法
引用方法實際就是方法調用操作,關注點返回值、入參和副作用的情況如下:
1. 方法的返回值將輸出到最終結果中
2. 入參的數據類型
$變量 或 $屬性,數據類型參考第一小節; 范圍操作符(如:[1..2]或[$arg1..$arg2]),將作為java.util.ArrayList處理 字典字面量(如:{a:"a",b:"b"}),將作為java.util.Map處理 數字字面量(如:1),將自動裝箱或拆箱匹配方法定義中的int或Integer入參
3. 副作用
// 若操作如java.util.Map.put方法,則會修改Java代碼部分中的Map對象鍵值對 $map.put("key", "new value")
指令主要用于定義重用模塊、引入外部資源、流程控制。指令以 # 作為起始字符。
1. #set:向引擎上下文對象添加屬性或對已有屬性進行修改
格式: #set($變量 = 值) ,具體示例如下:
#set($var1 = $other) #set($var1.prop1 = $other) #set($var = 1) #set($var = true) #set($var = [1,2]) #set($var = {a:"a", b:"b"}) #set($var = [1..3]) #set($var = [$arg1..$arg2]) #set($var = $var1.method()) #set($var = $arg1 + 1) #set($var = "hello") #set($var = "hello $var1") // 雙引號可實現字符串拼接(coffeescript也是這樣哦!),假設$var1為fsjohnhuang,則$var為hello fsjohnhuang #set($var = 'hello $var1') // 單引號將不解析其中引用,假設$var1為fsjohnhuang,則$var為hello $var1
作用域明顯是全局有效的。
格式:
#if(判斷條件) ......... #elseif(判斷條件) ......... #else ......... #end
通過示例了解判斷條件:
#if($name) //$name不為false、null和undefined則視為true $name #elseif($job == "net") // ==和!=兩邊的變量將調用其toString(),并對比兩者的返回值 Net工程師 #elseif($age <= 16) // 支持<=,>=,>,<邏輯運算符 未成年勞工 #elseif(!$married) // 支持!邏輯運算符 未婚 #elseif($age >= 35 && !$married) // 支持&&,||關系運算符 大齡未婚青年 #end
格式:
#foreach($item in $items) .......... #end
$item 的作用范圍為#foreach循環體內。
$items 的數據類型為 Object[]數組 、 [1..2] 、 [1,2,3,4] 、 {a:"a",b:"b"} 和含 public Iterator iterator() 方法的對象,具體如下:
java.util.Collection子類,Velocity會調用其iterator方法獲取Iterator對象
java.util.Map子類,Velocity會調用value()獲取Collection對象,然后調用調用其iterator方法獲取Iterator對象
java.util.Iterator對象,直接將該Iterator對象添加到上下文對象中時,由于Iterator對象為只進不退的操作方式,因此無法被多個#foreach指令遍歷
java.util.Enumeration對象,直接將該Enumeration對象添加到上下文對象中時,由于Iterator對象為只進不退的操作方式,因此無法被多個#foreach指令遍歷
內置屬性$foreach.count ,用于指示當前循環的次數,從0開始。可以通過配置項 directive.foreach.maxloops 來限制最大的循環次數,默認值為-1(不限制)。
示例——使用Vector和Iterator的區別:
模板:
#macro(show) #foreach($letter in $letters) $letter #end #end #show() #show(
Java代碼部分:
Vector<String> v = new Vector<String>(); v.add("a"); v.add("b"); VelocityContext ctx = new VelocityContext(); ctx.put("letters",v); Template t = Velocity.getTemplate("模板路徑"); StringWriter sw = new StringWriter(); t.merge(ctx,sw); System.out.println(sw.toString()); // 結果 // a // b // a // b ctx.put("letters",v.iterator()); // 結果 // a //
#foreach($item in $items) #if($item == "over") #break; #end $item #end
#set($cmd="stop") $cmd #if($cmd == "stop") #stop #end $cmd // 該語句將不執行
6. #include:引入外部資源(引入的資源不被引擎所解析)
格式: #include(resource[ otherResource]*)
resource、otherResource可以為單引號或雙引號的字符串,也可以為$變量,內容為外部資源路徑。注意為相對路徑,則以引擎配置的文件加載器加載路徑作為參考系,而不是當前模板文件的路徑為參考系。
7. #parse:引入外部資源(引入的資源將被引擎所解析)
格式: #parse(resource)
resource可以為單引號或雙引號的字符串,也可以為$變量,內容為外部資源路徑。注意為相對路徑,則以引擎配置的文件加載器加載路徑作為參考系,而不是當前模板文件的路徑為參考系。
由于#parse指令可能會引起無限遞歸引入的問題,因此可通過配置項 directive.parse.max.depth來限制最大遞歸引入次數,默認值為10.
定義格式:
#macro(宏名 [$arg[ $arg]*]?)
.....
#end
調用格式:
#宏名([$arg[ $arg]]?)
示例1——定義與調用位于同一模板文件時,無需遵守先定義后使用的規則:
#log("What a happy day") #macro(log $msg) log message: $msg #end
示例2——定義與調用位于不同的模板文件時,需要遵守先定義后使用的規則:
## 模板文件macro.vm
#macro(log $msg)
log message: $msg
#end
## 模板文件main.vm #parse("macro.vm") #log("What a happy day")
原理解析:Velocity引擎會根據模板生成語法樹并緩沖起來然后再執行,因此宏定義和調用位于同一模板文件時,調用宏的時候它已經被引擎識別并初始化了(類似js中的hosit)。
若定義與調用位于不同的模板文件中時,由于 #parse 是引擎解析模板文件時才被執行來引入外部資源并對其中的宏定義進行初始化,因此必須遵循先定義后使用的規則。
我們可配置全局宏庫,配置方式如下:
Properties props = new Properties(); // velocimacro.library的值為模板文件的路徑,多個路徑時用逗號分隔 // velocimacro.library的默認值為VM_global_library.vm props.setProperty("velocimacro.library", "global_macro1.vm,global_macro2.vm"); VelocityEngine ve = new VelocityEngine(props);
另外#macro還有另一種傳參方式——$!bodyContent
#macro(say) $!bodyContent #end #@say()Hello World#end // 結果為Hello World
示例:
#define($log) hello log! #end $log
可視為弱版#macro,一般不用,了解就好了。
示例:
#set($name = "over") #evalute("#if($name=='over')over#{else}not over#end") // 輸出over
一般不用,了解就好了。
通過 \ 對 $ 和 #進行轉義,導致解析器不對其進行解析處理。
#set($test='hello') $test ## 結果:hello \$test ## 結果:$test \\$test ## 結果:\hello \\\$test ## 結果:\$test $!test ## 結果:hello $\!test ## 結果:$!test $\\!test ## 結果:$\!test $\\\!test ## 結果:$\\!test
八、總結
VTL語法部分KO了,接下來就是模板與宿主環境通信——核心在引擎上下文對象(VelocityContext)上!
尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/4112866.html ^_^肥仔John
文章列表