Velocity是一个基于Java的模板引擎,它可以使得前端的开发者轻松的引用后端Java代码中定义的方法。前端和后端的开发可以根据MVC模型并行的进行,这意味着Web开发工程师们可以更关注于自己的事情,并使得Web具有更好的可维护性。Velocity模板使用VTL(Velocity模板语言)编写。
这是一个最简单的例子,使用set指令定义了一个变量foo,变量以“$”符开头,当它被赋值后可以在你的HTML文档中的任何一个地方引用它。
<html>
<body>
#set( $foo = "百度" )
你好啊$foo!
</body>
<html>
页面输出结果为:
你好啊百度!
代码中的set指令一会儿我会更详细的说明。
第一种注释方法是以##开头,类似C++代码里的//。
## 这是一行注释呀这是一行注释
第二种注释方法是在段落的首位分别用#*和*#注释,当注释行数较多时用此方法更合适。
#*
这是很多行注释啊很多行注释
这是很多行注释啊很多行注释
这是很多行注释啊很多行注释
这是很多行注释啊很多行注释
这是很多行注释啊很多行注释
这是很多行注释啊很多行注释
*#
第三种注释方法主要用于设置一些文档的作者、版本等信息。
#**
这是个很强大很全面的多行注释啊多行注释
这是个很强大很全面的多行注释啊多行注释
这是个很强大很全面的多行注释啊多行注释
@author Bosn
@version 1.0.2
*#
引用
在VTL中有三种引用的类型:变量、属性和方法。在VTL中所有的类型在模板中都会解析为String,假设有一个对象$foo在Java代码中是整型,则Velocity将会调用它的.toString()方法将其转换为String。
VTL的命名规则很简单,第一个字符必须是字母(a..z或A..Z),其他的部分限于以下类型:
l 字母(a..z, A..Z)
l 数字(0..9)
l 减号(“-“)
l 下划线(“_”)
以下是标识符合法的变量:
l $foo
l $bosnMa
l $bosn-ma
l $bosn_ma
l $bosnMa1
变量既可以通过set指令赋值(FE),也可以通过Java代码赋值(RD)。例如,如果一个Java变量$foo的值为”abc”,当模板被请求时,所有页面上的$foo都将被赋值”abc”。
如果我在模板中使用以下语句:
$set($foo = “abc”)
效果和前者相同,但赋值的位置不同。
VTL变量的属性和其它语言类似,变量名之后紧接一个“.“和另一个变量标识符,一下是属性的示例:
$customer.Address
$purchase.Total
我们先看第一行,$customer.Address。这个语句有两个含义,它可以表示查找标识符为customer的哈希表并返回键为Address相对应的值,但$customer.Address也可以意指引用一个方法(引用方法下个部分会详细讨论)当页面被请求时,Velocity会决定两种可能哪一项有意义,并返回最合适的值。
VTL的方法(Method)的概念和其它语言相差无几,方法也是以$开头,后接VTL标识符和VTL方法体,最后有一对括号,括号内输入可选的参数列表。一下是合法的参数引用:
$customer.getAddress()
$purchase.getTotal()
$page.setTitle(“My Home Page”)
$person.setAttributes([“Strange”, “Weird”, “Excited”])
关于方法,比较有趣的一件事情就是VTL属性可以作为VTL方法的缩写。比如,属性$customer.Address和使用方法$customer.getAddress()等价。当发生冲突时(既有Address属性,又有getAddress()方法),使用$customer.Address会优先返回属性Address的值。属性和方法的主要区别是只有方法才可以为其输入参数列表。
之前我们提到过,Velocity会非常聪明的找到被请求的属性所对应的方法,它会基于命名规则尝试找到合适的选项。属性的首字母大写和小写的情况是要区分的,对于小写开头的属性,例如$customer.address,查找顺序依次为:
1.getaddress()
2.getAddress()
3.get(“address”)
4.isAddress()
对于大写开头的属性,例如$customer.Address,则查找顺序依次为:
1.getAddress()
2.getaddress()
3.get(“Address”)
4.isAddress()
在以上示例中我们引用变量时,使用“$”符紧接变量名,如$foo,除此之外还有一种正式的引用方法,如下所示:
${foo}
${customer.Address}
${customer.getAddress()}
对于一般的使用,之前的章节所讲述的方法是完全够用的,但有些特殊情况我们必须使用本节所讲的正式引用法。例如,我们引用一个变量$vice,对于如下语句:
Jack is a $vicemaniac.
$vice不能够正常解析,因为“vice”和文本“maniac”紧挨着,造成变量名被解析成$vicemaniac。正确的写法,是使用正式引用法:
Jack is a ${vice}maniac.
这样就可以区分特殊情况下的变量名和普通文本。
什么是静引用?比如,在模板中我们放置一个文本框
<input type=”text” name=”email” value=”$email” />
正常情况还好,后端Java Code或使用set都可以为$email赋值,可是一旦$email没有被赋值,字符串”$email”就会被显示出来,出于这方面的考虑,我们可以使用静引用,如下所示:
<input type=”text” name=”email” value=”$!email” />
在”$”和标识符之间用叹号隔开,这个时候当$email被赋值时和普通变量一样,但当$email没有被赋值时,$!email将默认为空字符串,也就是不会显示在页面上。
另外,正式引用法和静引用时可以同时使用的,如下所示:
<input type=”text” name=”email” value=”$!{email}” />
符号”$”和”#”是VTL的特殊字符,它们往往会被解释成VTL变量或指令的开头,所以在使用它们的时候要格外的小心。在文本中出现这些特殊字符的时候,我们要进行一些处理,告诉Velocity这些符号和普通文本无异。
例如,当我们写”Give me $9999 please!”这句话时不会出现什么问题,因为VTL标识符总是以字母开头,所以$9999不会被误解为变量引用,但当普通文本和现有变量引用存在冲突的时候,我们可以使用右斜杠“\”转义。例如,
#set( $email = “foo” )
$email
如果Velocity在你的模板中碰到$email,它将会搜索是否有对应的值。上例的输出为foo,因为$email已定义,如果没有定义,则将输出$email(若想未定义时不输出,使用上面讲过的静引用)。现在问题来了,如果$email已经定义了,但是我想输出文本“$email”,怎样实现?能解决这个问题的方法不只一种,其中最简单的方法就是使用转义符。
#set ($email = “foo” )
$email
\$email
\\$email
\\\$emailf
输出依次为
foo
$email
\foo
\$email
注意转义符的绑定顺序是从左至右,和C++类似, 前两行很容易看懂,第三行两个“\”将输出一个“\”,在第四行中,我们按照顺序分析,第一个“\”发现临近的一个“\”,结合输出一个“\”,之后第三个“\”临近一个“$”,结合,转义,输出一个“$”,然后输出普通文本“email”,最终结果为“\$email”。
对于未定义的引用要注意,比如
#set( $foo = “hello baidu”)
$undefined = $foo
这里的输出将会是:$defined = hello baidu,由于$undefined没有定义,所以Velocity会将它当做普通的文本处理,这点一定要注意。
在上面的属性一节中,我们已经介绍过了等价写法,这些写法主要是取自Java命名规则的优点,使模板的编写更简单。首先给出以下示例:
$foo
$foo.getBar()
##等价于
$foo.Bar
$data.setUser(“bosn”)
##等价于
$set( $data.User = “bosn”)
$data.getRequest().getServerName()
##等价于
$data.Request.ServerName
##等价于
${data.Request.ServerName}
VTL引用使得模板编写者能够为Web动态的生成内容,VTL指令总是以#开头,类似于VTL引用,指令也可以用花括号{}包围,同样,在某些特殊情况这种表示法会非常有用,如下所示,将会产生错误:
#if($a==1)true enough#elseno way!#end
这里原想当$a等于1时输出true enough,否则输出no way,但是#elseno被解析成一个引用,由于#if找不到对应的else,错误便产生。正确的写法如下所示:
#if($a==1)true enough#{else}no way!#end
细节决定成败,这些细节也是一旦出现,最让大家头疼的罪魁祸首,所以我们一定要尽可能的抓住这些常见的细节问题。
#set指令用于为变量赋值。变量可以被另外一个变量或属性赋值,例如:
#set( $primate = “monkey” )
#set( $customer.Behavior = $primate )
赋值时,左值必须是变量的引用或属性的引用,而右值可以是以下列表中的任意类型:
l 变量引用
l 字符串文本
l 属性引用
l 方法引用
l 数字文本
l 数组
l Map
下面让我们一起来看例子吧:
#set( $monkey = $bill ) ##变量引用
#set( $monkey.Friend = “monica” ) ##字符串文本
#set( $monkey.Blame = $whitehouse.Leak ) ##属性引用
#set( $monkey.Plan = $spindoctor.weave($web) ) ##方法引用
#set( $monkey.Number = 123 ) ##数字文本
#set( $monkey.Say = [“Not”, $my, “fault”] ) ##数组
#set( $monkey.Map = {“banana”:”good”, “roast beef”: “bad”}) ## Map
注意:用[..]定义的数组可以用ArrayList类定义的方法访问,例如上面的例子中的数组的第一个元素可以用#monkey.Say.get(0)访问。类似的,用{}操作符定义的Map可以使用Map类定义的方法,例如上面的例子中的Map的第一个元素可以用#monkey.Map.get(“banana”)访问。
除此之外右值还可以是一个简单的数学表达式:
#set( $value = $foo + 1 )
#set( $value = $bar – 1 )
#set( $value = $foo * $bar )
#set( $value = $foo / $bar )
这里又要提到一个需要注意的细节,当右值是一个为空或未定义的属性或方法的引用时,左值将不会被赋值,这个非常容易搞混。 我们先看例子:
#set( $query = {“name”:”Bosn”} ##这里定义了一个Map,只有一个键”name”,并且值为”Bosn”
#set( $result = $query(“name”) )
The result of the first query is $result
#set( $result = $query(“address”) ) ##注意query并没有”address”键
The result of the second query is $result
输出结果为:
The result of the first query is Bosn
The result of the second query is Bosn
这很容易令人疑惑,由于$query(“address”)为空,$result并没有被赋值,而不是像C++等其他语言那样将Null赋给$result,因此$result依旧保存旧值。我们再看一个例子:
#set( $query = {“name”:”Bosn”, “address”:”F5-BE329”}
#set ($keys = [“name”, “address”, “age”] ##定义了一个数组,存放要查找的所有键。
#foreach( $i in $keys)
#set( $result = $query.get($i) )
#if($result)
Query was successful
#end
#end
输出为:
Query was successful
Query was successful
Query was successful
有些同学可能会迷惑,key ”age”在query中未定义,为什么会输出三句“Query was successful”?由于循环在第二轮时$result被赋为键”address”对应的值”F5-BE329”,而query.get(“age”)为空,所以并不赋值。因此第三次循环$result依然为”F5-BE329”,所以依然会输出结果。为了解决这个问题,我们再每次循环的开头将$result赋值为false,如下所示:
#set( $query = {“name”:”Bosn”, “address”:”F5-BE329”}
#set ($keys = [“name”, “address”, “age”] ##定义了一个数组,存放要查找的所有键。
#foreach( $i in $keys)
#set( $result = false )
#set( $result = $query.get($i) )
#if($result)
Query was successful
#end
#end
输出为:
Query was successful
Query was successful
像#foreach、#if等指令都要由#end,而#set指令并不需要。
VTL的文本处理因为和其它语言有些细微差异,所以容易搞混,请各位同学认真区分。首先,在用#set指令时,双引号“”包围的文本中如果含有变量引用是会被解析的(C/C++/Java/C#都不会),例如:
#set( $directoryRoot = “www” )
#set( $templateName = “index.vm” )
#set( $template = “$directoryRoot / $templateName” )
输出为:
www / index.vm
但是以单引号’’包围的字符串将不会被解析:
#set( $directoryRoot = “www” )
#set( $templateName = “index.vm” )
#set( $template = “$directoryRoot / $templateName” )
输出为
$directoryRoot / $templateName
默认情况下,用单引号避免文本被解析的功能是开启的,但这个默认设置也可以在velocity.properties中修改,例如stringliterals.interpolate = false。
放置文本被解析的另一种方式是使用#literal命令,这在将多行字符串设置为当做文本处理时非常有用,例子如下:
#literal()
#foreach( $i in $myMap)
nothing will happen to $i
#end
#end
结果为:
#foreach( $i in $myMap)
nothing will happen to $i
#end
VTL中的#if指令类似于其它语言的if语句,例如:
#if( $foo )
<strong>Hello Baidu!</strong>
#end
当变量$foo为true($foo是boolean型,值为true,或者value为其它类型,值不为空时都将在if中返回true)时,结果为<strong>Hello Baidu!</strong>,否则结果为空。下面是使用elseif的例子,用法和其它编程语言相同,不再累述:
#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
直接上例子:
## logical AND
#if( $foo && $bar )
<strong> This AND that</strong>
#end
当且仅当$foo和$bar都返回true时,输出<strong> This AND that</strong>
,否则输出为空。
## logical OR
#if( $foo || $bar )
<strong>This OR That</strong>
#end
只要$foo和$bar有任何一个为真(包括都为真),输出<strong> This OR that</strong>
,否则输出为空。
##logical NOT
#if( !$foo )
<strong>NOT that</strong>
#end
当$foo为true时,输出为空,否则,输出<strong>NOT that</strong>。
#foreach循环,例子如下:
<ul>
#foreach( $product in $allProducts )
<li>$product</li>
#end
</ul>
该循环遍历数组$allProducts中的所有元素,和C#的foreach语句相同。
#include指令可以将本地文件导入到模板中该条指令所在位置。使用#include导入的内容将不会通过模板引擎,为了安全,只有在TEMPLATE_ROOT(模板的根目录及该目录的所有子目录、子子目录等)下的文件才可以使用#include导入。下面是例子:
#include( “one.text” )
如果要导入的文件不只一个,可以用逗号”,”隔开:
#include( “one.gif” , “two.txt” , “three.htm” )
我们也可以使用变量,在运行时识别导入的模板的文件名:
#include( “greetings.txt” , $seasonalstock )
#parse和#include类似,但是它允许导入的本地文件中包含有VTL指令,Velocity将会解析VTL指令。例如:
#parse( “me.vm” )
如同#include指令, #parse也可以将变量作为参数。任何使用#parse导入的文件都必须在TEMPLATE_ROOT下。不同于#include,#parse指令只允许带有一个参数。
使用#stop指令可以停止模板引擎的执行,这在调试的时候会很有用。
#macro指令可以用来定义宏,无论对于简单还是复杂的情形,宏都是广泛使用的非常强大的工具,我们可以这样定义一个宏:
#macro(d)
<tr><td></td></tr>
#end
括号中的d是所定义的宏的标识符(简单的说就是名字),我们可以这样调用一个宏:
#d()
当这个模板被请求时,Velocity会将#d()替换为宏d中的内容,即“<tr><td></td></tr>”。
宏是可以带任意数量的参数的,包括0个(如上面的例子),在下面的例子中,定义了一个带有两个参数的宏,第一个$color是 一个变量,第二个$somelist是一个数组。
#macro( tablerows $color $somelist )
#foreach( $something in $somelist )
<tr><td bgcolor=$color>$something</td></tr>
#end
#end
下面是调用这个宏的例子:
#set( $greatlakes = ["Superior","Michigan","Huron","Erie","Ontario"] )
#set( $color = "blue" )
<table>
#tablerows( $color $greatlakes )
</table>
最后将输出:
<table>
<tr><td bgcolor="blue">Superior</td></tr>
<tr><td bgcolor="blue">Michigan</td></tr>
<tr><td bgcolor="blue">Huron</td></tr>
<tr><td bgcolor="blue">Erie</td></tr>
<tr><td bgcolor="blue">Ontario</td></tr>
</table>
本教程由大灰狼Bosn编写,该教程是初级教程,意在为新手提供入门的引导,还有更多的语法结构等细节请参考其它资料。希望该教程能够为新手提供帮助。如果发现错误,欢迎批评指正。
E-mail:[email protected]
原文链接:http://www.cnblogs.com/bosnma/archive/2009/12/15/1624701.html
http://velocity.apache.org/engine/releases/velocity-1.5/user-guide.html