Velocity 語(yǔ)法筆記

前言

在參考官方的翻譯文檔時(shí), 很多語(yǔ)句的翻譯的還有點(diǎn)生硬, 而且我在學(xué)的過(guò)程中有些地方讀起來(lái)很吃力, 我相信也會(huì)有跟我一樣的朋友, 所以寫此文, 希望把表達(dá)的不清楚的地方能稍微的解釋的變清楚一些, 同時(shí)加上一些自己對(duì)該語(yǔ)法的理解和看法, 當(dāng)然, 不一定準(zhǔn)確, 僅代表個(gè)人觀點(diǎn).

set

#set( $a = "Velocity" )

這個(gè)VTL 語(yǔ)句, 像所有的VTL語(yǔ)句一樣绊含,通過(guò) # 字符開始并包含一個(gè)指令: set. 當(dāng)一個(gè)用戶訪問(wèn)你的頁(yè)面時(shí),Velocity模板 將在你的Web頁(yè)面中搜索所有的#字符, 然后認(rèn)為它是VTL語(yǔ)句的開始亦渗,但是#字符并沒(méi)有實(shí)際意義联四。

注釋

單行注釋

## 單行注釋

多行注釋

#*
    多行注釋, Velocity會(huì)忽略此段 
*#

多行文檔注釋

#**
    這種注釋類似于javadoc
    @author  somebody
    @version 1.7
*#

引用

vtl中引用分為三種, 變量, 屬性, 及方法

變量

當(dāng)VTL應(yīng)用一個(gè)變量時(shí), 例如$foo,這個(gè)變量可以獲取一個(gè)值從模板的 set指令中, 或者從Java代碼中。例如,假如在Java中定義了一個(gè)變量foo, java中定義的值就是Web頁(yè)面中所有的 $foo引用.
或者, 我在頁(yè)面中定義下面語(yǔ)句
#set( $foo = "bar" ),$foo輸出的結(jié)果將和你定義的是一樣的。

屬性

VTL中第二個(gè)特點(diǎn)鮮明的引用是屬性引用, 屬性有一個(gè)與眾不同的格式. 它的標(biāo)識(shí)符前面需要添加一個(gè)$變量標(biāo)識(shí)符, 緊跟著后面一個(gè)點(diǎn)(“.”) . 下面是一個(gè)在VTL中屬性引用的實(shí)例:
$customer.Address
在vtl中, 我的理解, 它即可以是customer的屬性Address, 也可以是customer.getAddress()方法... 比el表達(dá)式更強(qiáng)大, 你可以根據(jù)需求來(lái)返回你需要的值.

方法

方法跟java是一樣的, 也是可以傳參, 做很多事情

$customer.getAddress()
$purchase.getTotal()
$page.setTitle( "My Home Page" )
$person.setAttributes( ["Strange", "Weird", "Excited"] )

VTL方法和屬性的區(qū)別

VTL 屬性引用能夠被當(dāng)著方法引用的簡(jiǎn)寫. 屬性 $customer.Address 引用和 $customer.getAddress()方法的引用效果是一樣的. 一般情況下如果可以,我們通過(guò)簡(jiǎn)寫的方式來(lái)引用方法.屬性和方法主要不同是方法能夠引用參數(shù) .

下面的方法可以被屬性引用簡(jiǎn)寫

$sun.getPlanets()
$annelid.getDirt()
$album.getPhoto()

但是下面的方法,就不可以簡(jiǎn)寫

## 根據(jù)以下數(shù)組中參數(shù)獲取
$sun.getPlanet( ["Earth", "Mars", "Neptune"] )
## 添加一個(gè)fans
$user.addFans()
## 設(shè)定title
$book.setTitle( "Homage to Catalonia" )

假如你有一個(gè)引用在一個(gè)集合上 , 你就可以用集合對(duì)象的方法, 比如size,get(index),isEmpty等方法

$myarray.isEmpty()
$myarray.size()
$myarray.get(2)
$myarray.set(1, 'test')

支持可變參數(shù)

后臺(tái)對(duì)象中有一個(gè)setPhones(String... phones)的方法, vtl可以這樣用
$user.setPhones('13812345678', '15812345670', '18812345671')
$user.setPhones() 將會(huì)賦一個(gè)空數(shù)組

屬性調(diào)用規(guī)則

正如前面提到的, 屬性經(jīng)常涉及到父類方法的引用. Velocity是十分擅長(zhǎng)解決方法對(duì)應(yīng)的屬性獲取,它可以根據(jù)幾種不同的命名約定進(jìn)行選擇,準(zhǔn)確的查找規(guī)則依賴是否屬性的名字以大寫開始憔足。對(duì)于小寫名字,例如 $customer.address, 調(diào)用的順序是

getaddress()
getAddress()
get(“address”)
isAddress()
對(duì)于大寫的屬性名字像 $customer.Address, 它稍微不同:
getAddress()
getaddress()
get(“Address”)
isAddress()

渲染

每一個(gè)引用的值(變量滓彰,屬性揭绑,或者方法)都被轉(zhuǎn)換為一個(gè)字符串并作為最終的輸出他匪。假如這里有一個(gè)對(duì)象表示為$foo (例如一個(gè)整數(shù)對(duì)象), 當(dāng)Velocity調(diào)用它時(shí)夸研,Velocity會(huì)調(diào)用它的.toString() 方法轉(zhuǎn)化為字符串.

索引標(biāo)識(shí)符

$userList[2].name等同于 $userList.get(2).name 一看就明白
$userMap["name"] 等同于userMap.get("name") 跟java代碼都是一樣的,非常方便

這相同的語(yǔ)法也能夠使用在Java數(shù)組上因?yàn)橛捎赩elocity封裝了數(shù)組在訪問(wèn)對(duì)象上提供了一個(gè)get(Integer)方法,它能返回一個(gè)特殊的元素悼沈。

$foo.bar[1].junk
$foo.callMethod()[1]
$foo["apple"][4]

一個(gè)引用也能夠通過(guò)索引來(lái)進(jìn)行賦值, 例如:

#set($foo[0] = 1)
#set($foo.bar[1] = 3)
#set($map["apple"] = "orange")

正式引用標(biāo)識(shí)符 很重要

在大部分情況下你能夠使用標(biāo)識(shí)符引用井辆,但是有些情況下要求正確的符號(hào)被要求正確的處理杯缺。我認(rèn)為, 應(yīng)該盡量用${}來(lái)寫會(huì)比較好
一個(gè)簡(jiǎn)單的例子:
Jack is a $vicemaniac.
Jack is a ${vice}maniac.
以上兩者的變量的引用是不一樣的, 這你就應(yīng)該看懂了, 為什么用正式引用標(biāo)識(shí)符比較好的原因.

靜態(tài)引用標(biāo)識(shí)符

當(dāng)Velocity遇到一個(gè)沒(méi)有定義的引用時(shí),正常的是按照原文輸出的. 例如, 假如下面的引用是VTL模板的一部分.
<input type="text" name="email" value="${email}"/>
按照道理, 當(dāng)email沒(méi)有值時(shí), 我們希望的是value="", 但實(shí)際上vlt會(huì)在無(wú)值時(shí)輸出"$email".... 這就很尷尬了...相比el表達(dá)式就沒(méi)有這個(gè)問(wèn)題對(duì)吧.
<input type="text" name="email" value="$!{email}"/>
這么寫, 就可以解決這個(gè)問(wèn)題, 在$后加上!就好了..

模式替換或者說(shuō)多種寫法

Velocity引用利用了一些Java的設(shè)計(jì)原則進(jìn)行設(shè)計(jì),很容易使用才写。 例如:

$foo.getBar()
## 等價(jià)于
$foo.Bar

$data.setUser("jon")
## 等價(jià)于
#set( $data.User = "jon" )

$data.getRequest().getServerName()
## 等價(jià)于
$data.Request.ServerName
## 等價(jià)于
${data.Request.ServerName}

指令

指令一直以 #開始.

#if ($userList.size() > 3) 
   用戶多于3人的情況
#else 
   用戶未達(dá)標(biāo)
#end
模板會(huì)根據(jù)userList的長(zhǎng)度, 輸出以上兩句話中的一句.

像引用一樣,指令的名字可能是相等的通過(guò){ 和 } 符號(hào). 這是好的方式

#{if} ($userList.size() > 3) 
   用戶多于3人的情況
#{else}
   用戶未達(dá)標(biāo) 
#{end}

set指令

#set 指令被用來(lái)設(shè)定一個(gè)引用的值. 這個(gè)值能夠被分配一個(gè)變量引用或者屬性引用,這種情況發(fā)生在括號(hào)中, 如下實(shí)例:

#set( $primate = "monkey" )
#set( $customer.Behavior = $primate )

左邊的(LHS)必須分配一個(gè)變量引用或者屬性引用. 右邊的(RHS)可以是以下類型:

Variable reference 
String literal  
Property reference
Method reference
Number literal
ArrayList
Map

其實(shí)就跟java一樣, 就是賦值左邊要寫屬性, 右邊賦值可以是以上幾種類型, 下面是關(guān)于幾種類型賦值的寫法, 用時(shí)參考就好了:

#set( $monkey = $bill ) ## variable reference
#set( $monkey.Friend = "monica" ) ## string literal
#set( $monkey.Blame = $whitehouse.Leak ) ## property reference
#set( $monkey.Plan = $spindoctor.weave($web) ) ## method reference
#set( $monkey.Number = 123 ) ##number literal
#set( $monkey.Say = ["Not", $my, "fault"] ) ## ArrayList
#set( $monkey.Map = {"banana" : "good", "roast beef" : "bad"}) ## Map

同時(shí)右邊的值也能使用簡(jiǎn)單的算術(shù)表達(dá)式, 這個(gè)跟el表達(dá)式也是一樣的道理:

#set( $value = $foo + 1 )
#set( $value = $bar - 1 )
#set( $value = $foo * $bar )
#set( $value = $foo / $bar )

另外 RHS如果是null值 則不會(huì)分配個(gè)LHS

#set( $result = $query.criteria("name") )
第一次輸出result 的值: $result

#set( $result = $query.criteria("address") )
第二次輸出result 的值: $result
如果$query.criteria("name") 的值是張三, 而$query.criteria("address")的值是null的話, 則輸出結(jié)果是:
第一次輸出result 的值: 張三
第二次輸出result 的值: 張三
也就是說(shuō), 第二次#set指令并沒(méi)有改變r(jià)esult的值.

再看下面的例子, 意圖應(yīng)該:

#set( $monkey = {}) ## 初始化monkey
#set( $monkey.name = "猴子" ) 
##下面準(zhǔn)備遍歷這個(gè)$monkey的key
#set ( $conditionKey = ["name", "food"]) 

#foreach ($key in $conditionKey)
    #set ($result = $monkey[$key])
    #if ($result)
        輸出:該key有值
    #end
#end
輸出結(jié)果應(yīng)該是: 
該key有值
該key有值
這個(gè)結(jié)果顯然不正確的, 因?yàn)?monkey.food 是沒(méi)有值的
如果要解決這個(gè)問(wèn)題, 那么就需要給$result提前賦值一個(gè)false
#foreach ($key in $conditionKey)
    #set ($result = false)
    #set ($result = $monkey[$key])
    #if ($result)
        輸出:該key有值
    #end
#end
這樣的話, 利用#set指令的特性, 不會(huì)賦null值, 則在key == food時(shí), $result的結(jié)果依然是false

字面量, 字符串的操作

#set指令的時(shí)候, 在雙引號(hào)里面的字符串將被解析, 如下所示:

 #set(  $directoryRoot = "www" )
#set(  $templateName = "index.vm" )
#set( $template = "$directoryRoot/$templateName" )
$template
輸出結(jié)果:
www/index.vm

然而, 當(dāng)字符串處在單引號(hào)中, 它將不被解析:

 #set( $foo = "bar" )
$foo
#set( $blargh = '$foo' )
輸出結(jié)果:
bar
$foo

默認(rèn)情況, 這種特征使用單引號(hào)不解析Velocity中的可用變量. 你也可以通過(guò)改變velocity.properties 中的stringliterals.interpolate=false配置來(lái)改變這種默認(rèn)設(shè)置.
或者, #[[不要解析此段代碼]]# 語(yǔ)法準(zhǔn)許模板設(shè)計(jì)者很容易的使用大量的語(yǔ)句塊,而這些語(yǔ)句塊中的變量不會(huì)被解析.

#[[
#foreach ($woogie in $boogie)
  nothing will happen to $woogie
#end
]]#
所以, 以上這段代碼就會(huì)在頁(yè)面輸出

條件語(yǔ)句

If / ElseIf / Else

不管從語(yǔ)法, 還是邏輯運(yùn)算符, 用法同java, 沒(méi)什么可說(shuō)的

#if( $foo < 10 )
    <strong>Go North</strong>
#elseif( $foo == 10 )
    <strong>Go East</strong>
#elseif( $bar == 6 )
    <strong>Go South</strong>
#else
    <strong>Go West</strong>
#end
這就是一組if else基本的寫法

關(guān)系和邏輯操作符

Velocity 使用等號(hào)決定兩個(gè)變量之間的關(guān)系. 下面簡(jiǎn)單實(shí)例展示了等會(huì)的怎么使用.

#set ($foo = "deoxyribonucleic acid")
#set ($bar = "ribonucleic acid")

#if ($foo == $bar)
  如果兩者相等輸出此處
#else
  否則輸出此處
#end

邏輯操作符 AND, OR 和NOT 操作符.

vtl的邏輯操作符同java一樣, 并且也有短路的特性, 不懂的補(bǔ)習(xí)一下java的基礎(chǔ)知識(shí)

#if( $foo && $bar )
   <strong> This AND that</strong>
#end
and邏輯操作符, 如果 $foo為false,表達(dá)式的結(jié)果為 false; $bar將不會(huì)計(jì)算. 這就是and短路特性
or邏輯操作符, 如果 $foo為true,表達(dá)式的結(jié)果為 true; $bar將不會(huì)計(jì)算. 這就是or短路特性

循環(huán)

#foreach元素用來(lái)循環(huán)操作

<ul>
#foreach( $product in $allProducts )
    <li>$product</li>
#end
#foreach循環(huán) $allProducts列表 . 每次遍歷, $allProducts 里面的單個(gè)值將被賦值給$product.
</ul>

上面這是List或者數(shù)組的寫法, 下面是map的寫法

<ul>
#foreach( $key in $allProducts.keySet() )
    <li>鍵: $key -> 值: $allProducts.get($key)</li>
#end
</ul>

Velocity提供了一種簡(jiǎn)單的循環(huán)計(jì)數(shù)以至于你能夠做一些事情,如下所示:

<table>
#foreach( $customer in $customerList )
    <tr><td>$foreach.count</td><td>$customer.Name</td></tr>
#end
</table>

Velocity也提供了一種簡(jiǎn)單的方式來(lái)判斷是否是最后一次迭代

#foreach( $user in $userList )
    $user.name 
    #if( !$foreach.hasNext ) 
        我是最后一個(gè)了
    #end
#end

如果你想從零開始的#foreach循環(huán), 你可以使用 $foreach.index 代替$foreach.count. 同樣的, $foreach.first 和 $foreach.last 也提供了$foreach.hasNext方式.如果你想訪問(wèn) #foreach外面的這些屬性, 你能夠引用它們通過(guò) $foreach.parent或 $foreach.topmost 屬性 (e.g. $foreach.parent.index 或者 $foreach.topmost.hasNext). 你也可以設(shè)置最大的循環(huán)執(zhí)行次數(shù). 默認(rèn)情況下沒(méi)有設(shè)置 (可以指定一個(gè)值 0 或者更小的), 可以設(shè)置一個(gè)數(shù)字在velocity.properties 的配置文件里面.
directive.foreach.maxloops = -1

你想停止一個(gè)foreach 循環(huán)在你的模板中, 你可以使用 #break指令在任何時(shí)候停止循環(huán):

#foreach( $customer in $customerList )
    #if( $foreach.count > 5 )
        #break
    #end
    $customer.Name
#end

引入

#include腳本元素準(zhǔn)許設(shè)計(jì)者引入一個(gè)本地文件, 然后插入到你#include 指令所在的地方。文件的內(nèi)容不經(jīng)過(guò)模板引擎處理. 由于安全的原因,這個(gè)文件僅僅能夠放在 TEMPLATE_ROOT下面.
引用多個(gè)文件:
#include( "one.gif","two.txt","three.htm" )

變量也可以作為文件名
#include( "greetings.txt", $seasonalstock )

解析

#parse腳本元素準(zhǔn)許模板設(shè)計(jì)者引用一個(gè)包含VTL的本地文件牙肝。Velocity將解析其中的VTL并輸出里面的元素配椭。
#parse( "me.vm" )
注意:任何被 #parse 指令引用的模板必須放在TEMPLATE_ROOT下面.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吱雏,隨后出現(xiàn)的幾起案子歧杏,更是在濱河造成了極大的恐慌犬绒,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拗秘,死亡現(xiàn)場(chǎng)離奇詭異扮匠,居然都是意外死亡餐禁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)筑舅,“玉大人版仔,你說(shuō)我怎么就攤上這事∪幌耄” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵晴竞,是天一觀的道長(zhǎng)表窘。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任逆济,我火速辦了婚禮,結(jié)果婚禮上雕欺,老公的妹妹穿的比我還像新娘。我一直安慰自己撞蜂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布蝌诡。 她就那樣靜靜地躺著溉贿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浦旱。 梳的紋絲不亂的頭發(fā)上宇色,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼宣蠕。 笑死例隆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的抢蚀。 我是一名探鬼主播镀层,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼皿曲!你這毒婦竟也來(lái)了唱逢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤屋休,失蹤者是張志新(化名)和其女友劉穎坞古,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劫樟,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡痪枫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叠艳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奶陈。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖虑绵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情闽烙,我是刑警寧澤翅睛,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站黑竞,受9級(jí)特大地震影響捕发,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜很魂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一扎酷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧遏匆,春花似錦法挨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至帝蒿,卻和暖如春荐糜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工暴氏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留延塑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓答渔,卻偏偏與公主長(zhǎng)得像关带,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子研儒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理豫缨,服務(wù)發(fā)現(xiàn),斷路器端朵,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • 前言 人生苦多好芭,快來(lái) Kotlin ,快速學(xué)習(xí)Kotlin冲呢! 什么是Kotlin舍败? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,146評(píng)論 9 118
  • 1. 簡(jiǎn)介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL敬拓、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,425評(píng)論 0 4
  • 閑趣之余心生一問(wèn)邻薯,不知諸位眼中萬(wàn)獸之王該是何樣的呢?高大威猛四字大約是對(duì)其的第一印象吧,散發(fā)著耀眼強(qiáng)大的光芒乘凸,立于...
    超級(jí)超級(jí)酷的余丹呀閱讀 342評(píng)論 0 0
  • 學(xué)了第五單元厕诡,我懂得了生命與水的關(guān)系,沒(méi)有水就沒(méi)有我們?nèi)祟愑冢瑳](méi)有水灵嫌,人們就會(huì)死亡 我學(xué)了《古詩(shī)二首》過(guò)分水嶺,飲湖...
    王明亮_fcc0閱讀 160評(píng)論 2 2