惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
Last Week in AI
Last Week in AI
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
H
Help Net Security
F
Fortinet All Blogs
MyScale Blog
MyScale Blog
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 司徒正美
量子位
N
Netflix TechBlog - Medium
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
Recorded Future
Recorded Future
博客园 - 三生石上(FineUI控件)
Vercel News
Vercel News
aimingoo的专栏
aimingoo的专栏
I
InfoQ
Microsoft Security Blog
Microsoft Security Blog
Scott Helme
Scott Helme
The Last Watchdog
The Last Watchdog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
AI
AI
WordPress大学
WordPress大学
Security Archives - TechRepublic
Security Archives - TechRepublic
Google Online Security Blog
Google Online Security Blog
U
Unit 42
V2EX - 技术
V2EX - 技术
MongoDB | Blog
MongoDB | Blog
Schneier on Security
Schneier on Security
博客园 - Franky
H
Heimdal Security Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
W
WeLiveSecurity
P
Privacy & Cybersecurity Law Blog
Cloudbric
Cloudbric
B
Blog RSS Feed
N
News | PayPal Newsroom
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
I
Intezer
Hacker News - Newest:
Hacker News - Newest: "LLM"
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
罗磊的独立博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
雷峰网
雷峰网

博客园 - EricGu

DES加密解密函数 - EricGu - 博客园 Hibernate Annotation笔记 Groovy和Grails配置方法 Log4net的用法 在vs2008中设置jquery智能提示 开发人员最喜爱的十大免费的Visual Studio插件 优化JavaScript代码 配置nhibernate时报 No persister for: NH.Model.Entities.Customer 异常 - EricGu 解决UpdatePanle中执行后台时间密码域丢失问题 网页制作技巧收集 Virtual PC 2007 SP1虚拟机上安装Ubuntu 9.04桌面版 JavaScript && CSS / CSS中的点滴 oracle10g中的5个服务的作用 ORACLE服务的作用 关于TreeView控件发布后无法显示展开图标的问题 怎样设计网页安全字体 Div+CSS网页制作误区 未来五年程序员应当具备的十项技能 标准W3C盒子模型
Velocity快速指南
EricGu · 2009-12-15 · via 博客园 - EricGu

什么是Velocity?

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,假设有一个对象$fooJava代码中是整型,则Velocity将会调用它的.toString()方法将其转换为String

变量

VTL的命名规则很简单,第一个字符必须是字母(a..zA..Z),其他的部分限于以下类型:

字母(a..z, A..Z

数字(0..9)

减号(“-“)

下划线(“_”)

以下是标识符合法的变量:

$foo

$bosnMa

$bosn-ma

$bosn_ma

$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指令用于为变量赋值。变量可以被另外一个变量或属性赋值,例如:

#set( $primate = “monkey” )
#set( $customer.Behavior = $primate )

赋值时,左值必须是变量的引用或属性的引用,而右值可以是以下列表中的任意类型:

变量引用

字符串文本

属性引用

方法引用

数字文本

数组

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

条件分支

If/ElseIf/Else

VTL中的#if指令类似于其它语言的if语句,例如:

#if( $foo )
         <strong>Hello Baidu!</strong>
#end

当变量$footrue$fooboolean型,值为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语句相同。

IncludeParse指令

#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:bosnma@live.cn
原文链接:http://www.cnblogs.com/bosnma/archive/2009/12/15/1624701.html

参考文献

http://velocity.apache.org/engine/releases/velocity-1.5/user-guide.html