Velocity 语法笔记

前言

在参考官方的翻译文档时, 很多语句的翻译的还有点生硬, 而且我在学的过程中有些地方读起来很吃力, 我相信也会有跟我一样的朋友, 所以写此文, 希望把表达的不清楚的地方能稍微的解释的变清楚一些, 同时加上一些自己对该语法的理解和看法, 当然, 不一定准确, 仅代表个人观点.

set

#set( $a = "Velocity" )

这个VTL 语句, 像所有的VTL语句一样,通过 # 字符开始并包含一个指令: set. 当一个用户访问你的页面时,Velocity模板 将在你的Web页面中搜索所有的#字符, 然后认为它是VTL语句的开始,但是#字符并没有实际意义。

注释

单行注释

## 单行注释

多行注释

#*
    多行注释, Velocity会忽略此段 
*#

多行文档注释

#**
    这种注释类似于javadoc
    @author  somebody
    @version 1.7
*#

引用

vtl中引用分为三种, 变量, 属性, 及方法

变量

当VTL应用一个变量时, 例如$foo,这个变量可以获取一个值从模板的 set指令中, 或者从Java代码中。例如,假如在Java中定义了一个变量foo, java中定义的值就是Web页面中所有的 $foo引用.
或者, 我在页面中定义下面语句
#set( $foo = "bar" ),$foo输出的结果将和你定义的是一样的。

属性

VTL中第二个特点鲜明的引用是属性引用, 属性有一个与众不同的格式. 它的标识符前面需要添加一个$变量标识符, 紧跟着后面一个点(“.”) . 下面是一个在VTL中属性引用的实例:
$customer.Address
在vtl中, 我的理解, 它即可以是customer的属性Address, 也可以是customer.getAddress()方法... 比el表达式更强大, 你可以根据需求来返回你需要的值.

方法

方法跟java是一样的, 也是可以传参, 做很多事情

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

VTL方法和属性的区别

VTL 属性引用能够被当着方法引用的简写. 属性 $customer.Address 引用和 $customer.getAddress()方法的引用效果是一样的. 一般情况下如果可以,我们通过简写的方式来引用方法.属性和方法主要不同是方法能够引用参数 .

下面的方法可以被属性引用简写

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

但是下面的方法,就不可以简写

## 根据以下数组中参数获取
$sun.getPlanet( ["Earth", "Mars", "Neptune"] )
## 添加一个fans
$user.addFans()
## 设定title
$book.setTitle( "Homage to Catalonia" )

假如你有一个引用在一个集合上 , 你就可以用集合对象的方法, 比如size,get(index),isEmpty等方法

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

支持可变参数

后台对象中有一个setPhones(String... phones)的方法, vtl可以这样用
$user.setPhones('13812345678', '15812345670', '18812345671')
$user.setPhones() 将会赋一个空数组

属性调用规则

正如前面提到的, 属性经常涉及到父类方法的引用. Velocity是十分擅长解决方法对应的属性获取,它可以根据几种不同的命名约定进行选择,准确的查找规则依赖是否属性的名字以大写开始。对于小写名字,例如 $customer.address, 调用的顺序是

getaddress()
getAddress()
get(“address”)
isAddress()
对于大写的属性名字像 $customer.Address, 它稍微不同:
getAddress()
getaddress()
get(“Address”)
isAddress()

渲染

每一个引用的值(变量,属性,或者方法)都被转换为一个字符串并作为最终的输出。假如这里有一个对象表示为$foo (例如一个整数对象), 当Velocity调用它时,Velocity会调用它的.toString() 方法转化为字符串.

索引标识符

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

这相同的语法也能够使用在Java数组上因为由于Velocity封装了数组在访问对象上提供了一个get(Integer)方法,它能返回一个特殊的元素。

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

一个引用也能够通过索引来进行赋值, 例如:

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

正式引用标识符 很重要

在大部分情况下你能够使用标识符引用,但是有些情况下要求正确的符号被要求正确的处理。我认为, 应该尽量用${}来写会比较好
一个简单的例子:
Jack is a $vicemaniac.
Jack is a ${vice}maniac.
以上两者的变量的引用是不一样的, 这你就应该看懂了, 为什么用正式引用标识符比较好的原因.

静态引用标识符

当Velocity遇到一个没有定义的引用时,正常的是按照原文输出的. 例如, 假如下面的引用是VTL模板的一部分.
<input type="text" name="email" value="${email}"/>
按照道理, 当email没有值时, 我们希望的是value="", 但实际上vlt会在无值时输出"$email".... 这就很尴尬了...相比el表达式就没有这个问题对吧.
<input type="text" name="email" value="$!{email}"/>
这么写, 就可以解决这个问题, 在$后加上!就好了..

模式替换或者说多种写法

Velocity引用利用了一些Java的设计原则进行设计,很容易使用。 例如:

$foo.getBar()
## 等价于
$foo.Bar

$data.setUser("jon")
## 等价于
#set( $data.User = "jon" )

$data.getRequest().getServerName()
## 等价于
$data.Request.ServerName
## 等价于
${data.Request.ServerName}

指令

指令一直以 #开始.

#if ($userList.size() > 3) 
   用户多于3人的情况
#else 
   用户未达标
#end
模板会根据userList的长度, 输出以上两句话中的一句.

像引用一样,指令的名字可能是相等的通过{ 和 } 符号. 这是好的方式

#{if} ($userList.size() > 3) 
   用户多于3人的情况
#{else}
   用户未达标 
#{end}

set指令

#set 指令被用来设定一个引用的值. 这个值能够被分配一个变量引用或者属性引用,这种情况发生在括号中, 如下实例:

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

左边的(LHS)必须分配一个变量引用或者属性引用. 右边的(RHS)可以是以下类型:

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

其实就跟java一样, 就是赋值左边要写属性, 右边赋值可以是以上几种类型, 下面是关于几种类型赋值的写法, 用时参考就好了:

#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

同时右边的值也能使用简单的算术表达式, 这个跟el表达式也是一样的道理:

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

另外 RHS如果是null值 则不会分配个LHS

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

#set( $result = $query.criteria("address") )
第二次输出result 的值: $result
如果$query.criteria("name") 的值是张三, 而$query.criteria("address")的值是null的话, 则输出结果是:
第一次输出result 的值: 张三
第二次输出result 的值: 张三
也就是说, 第二次#set指令并没有改变result的值.

再看下面的例子, 意图应该:

#set( $monkey = {}) ## 初始化monkey
#set( $monkey.name = "猴子" ) 
##下面准备遍历这个$monkey的key
#set ( $conditionKey = ["name", "food"]) 

#foreach ($key in $conditionKey)
    #set ($result = $monkey[$key])
    #if ($result)
        输出:该key有值
    #end
#end
输出结果应该是: 
该key有值
该key有值
这个结果显然不正确的, 因为$monkey.food 是没有值的
如果要解决这个问题, 那么就需要给$result提前赋值一个false
#foreach ($key in $conditionKey)
    #set ($result = false)
    #set ($result = $monkey[$key])
    #if ($result)
        输出:该key有值
    #end
#end
这样的话, 利用#set指令的特性, 不会赋null值, 则在key == food时, $result的结果依然是false

字面量, 字符串的操作

#set指令的时候, 在双引号里面的字符串将被解析, 如下所示:

 #set(  $directoryRoot = "www" )
#set(  $templateName = "index.vm" )
#set( $template = "$directoryRoot/$templateName" )
$template
输出结果:
www/index.vm

然而, 当字符串处在单引号中, 它将不被解析:

 #set( $foo = "bar" )
$foo
#set( $blargh = '$foo' )
输出结果:
bar
$foo

默认情况, 这种特征使用单引号不解析Velocity中的可用变量. 你也可以通过改变velocity.properties 中的stringliterals.interpolate=false配置来改变这种默认设置.
或者, #[[不要解析此段代码]]# 语法准许模板设计者很容易的使用大量的语句块,而这些语句块中的变量不会被解析.

#[[
#foreach ($woogie in $boogie)
  nothing will happen to $woogie
#end
]]#
所以, 以上这段代码就会在页面输出

条件语句

If / ElseIf / Else

不管从语法, 还是逻辑运算符, 用法同java, 没什么可说的

#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基本的写法

关系和逻辑操作符

Velocity 使用等号决定两个变量之间的关系. 下面简单实例展示了等会的怎么使用.

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

#if ($foo == $bar)
  如果两者相等输出此处
#else
  否则输出此处
#end

逻辑操作符 AND, OR 和NOT 操作符.

vtl的逻辑操作符同java一样, 并且也有短路的特性, 不懂的补习一下java的基础知识

#if( $foo && $bar )
   <strong> This AND that</strong>
#end
and逻辑操作符, 如果 $foo为false,表达式的结果为 false; $bar将不会计算. 这就是and短路特性
or逻辑操作符, 如果 $foo为true,表达式的结果为 true; $bar将不会计算. 这就是or短路特性

循环

#foreach元素用来循环操作

<ul>
#foreach( $product in $allProducts )
    <li>$product</li>
#end
#foreach循环 $allProducts列表 . 每次遍历, $allProducts 里面的单个值将被赋值给$product.
</ul>

上面这是List或者数组的写法, 下面是map的写法

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

Velocity提供了一种简单的循环计数以至于你能够做一些事情,如下所示:

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

Velocity也提供了一种简单的方式来判断是否是最后一次迭代

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

如果你想从零开始的#foreach循环, 你可以使用 $foreach.index 代替$foreach.count. 同样的, $foreach.first 和 $foreach.last 也提供了$foreach.hasNext方式.如果你想访问 #foreach外面的这些属性, 你能够引用它们通过 $foreach.parent或 $foreach.topmost 属性 (e.g. $foreach.parent.index 或者 $foreach.topmost.hasNext). 你也可以设置最大的循环执行次数. 默认情况下没有设置 (可以指定一个值 0 或者更小的), 可以设置一个数字在velocity.properties 的配置文件里面.
directive.foreach.maxloops = -1

你想停止一个foreach 循环在你的模板中, 你可以使用 #break指令在任何时候停止循环:

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

引入

#include脚本元素准许设计者引入一个本地文件, 然后插入到你#include 指令所在的地方。文件的内容不经过模板引擎处理. 由于安全的原因,这个文件仅仅能够放在 TEMPLATE_ROOT下面.
引用多个文件:
#include( "one.gif","two.txt","three.htm" )

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

解析

#parse脚本元素准许模板设计者引用一个包含VTL的本地文件。Velocity将解析其中的VTL并输出里面的元素。
#parse( "me.vm" )
注意:任何被 #parse 指令引用的模板必须放在TEMPLATE_ROOT下面.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,119评论 9 118
  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,398评论 0 4
  • 闲趣之余心生一问,不知诸位眼中万兽之王该是何样的呢?高大威猛四字大约是对其的第一印象吧,散发着耀眼强大的光芒,立于...
    超级超级酷的余丹呀阅读 339评论 0 0
  • 学了第五单元,我懂得了生命与水的关系,没有水就没有我们人类,没有水,人们就会死亡 我学了《古诗二首》过分水岭,饮湖...
    王明亮_fcc0阅读 157评论 2 2