[关闭]
@code33 2015-11-22T12:54:33.000000Z 字数 6564 阅读 1616

Velocity 学习笔记

学习笔记 Velocity

Create By Jyo on 2015-11-22
技能级别要求:熟练 -- 精通

前言:
Velocity是什么?

Velocity是Java服务端用于构筑view层的模板脚本语言,它和JS的区别是由服务端渲染,即爬虫和搜索引擎是可直接抓取并执行静态解析的;

为什么要投入成本学习Velocity,如果不学习和使用Velocity将要付出什么代价

在实际生产中,很多页面是来自于组件的组装,如果其中一个组件有需求需要更新,那么势必将要对一系列调用该组件的页面一并给予更新迭代操作,如果没有Velocity,那么将投入更大的工作量去对已有的页面进行维护操作.当然不用Velocity的话也可以通过其它方式实现jsp组件构建用以复用,比如对jsp页面封装,再通过一套类以对其维护.但学习成本<维护成本 so...你懂得

RHS:等式右边的值
LHS:等式左边的值

01.注释

Velocity的单行注释,使用##
多行注释使用#* cooments *#

02.引用

在Velocity Template Language(VTL)中,有三种类型的引用:变量,属性,方法。所有的引用都被看作是字符串,如果某个引用是一个整型值,velocity会调用它的toString()方法,将它转换为String。

变量:

变量是由$和紧跟其后的VTL标识符组成的。而VTL标识符必须以字母开头,后面可以包含并且仅能包含字母、数字、下划线 和减号。像$2.4这样的字符串,是不会被识别为变量的,因为2.4不是一个VTL标识符。

属性:

属性是由四部分组成,依次是$, 紧跟其后的VTL标识符,小数点,紧跟其后的VTL标识符。比如$Foo.bar。实际上,这个属性有两个含义,它可以表示在Foo标记的hashtable中寻找并返回bar这个key对应的value,也可以表示$Foo.getbar()这个方法(此方法在java对象中定义)。

方法:

方法是由四部分组成,依次是$,紧随其后的VTL标识符,小数点,紧随其后的函数调用。这里的函数调用是可以带参数的。比如$Foo.getBar("test")

很显然,属性有时是方法的一种简写,$Foo.bar$Foo.getbar()是等价的,这种情况下,尽量使用属性的形式。但是方法有参数的话,就没有和它等价的属性了。

属性的搜索规则:

属性可以表示为调用方法,也可表示从hashtable中找值,对于$Foo.bar来说,Velocity会按照以下顺序解析:

$Foo.getbar(), $Foo.getBar(), $Foo.get("bar"), $Foo.isBar()

而对于$Foo.Bar来说,Velocity会按照以下顺序解析:

$Foo.getBar(), $Foo.getbar(), $Foo.get("Bar"), $Foo.isBar()

所以,前两个是寻找方法,第三个是在hashtable中找值,第四个是一个is判断。

另外需要注意的是,属性不会被解析为对象的实例变量。$Foo.bar可以表示$Foo.getbar()或者$Foo.get("bar"),但是它不表示Foo这个java对象的bar属性。

规范的引用写法:

$Foo是一个简化后的写法,规范的写法应该是${Foo}。大部分情况下,简化写法是可以的,但是当一个引用后面紧跟一个VTL标识符时,规范写法就是必要的了。比如$Footic${Foo}tic就是两种不同的含义。所以,尽量还是使用规范写法,它永远不会有错。属性和方法的规范写法是${Foo.bar}以及${Foo.bar()}

Quiet Reference Notation(默参符):

如果一个引用(不管哪种类型)没有被定义,那么在使用时,它就没有值。Velocity默认的做法是,将这个引用作为普通字符串对待。比如,在<input type="text" name="email" value="$email" />中,如果$email并没有定义,那么这个文本框的value就是$email这个字符串。如果用$!email替代$email,那么输出将会是空字符串,而不是$email。对应的规范写法是$!{email}

03.字面值和转义

货币符号:

$是美元的符号,在文本中出现的"$2.5"这样的字符串,是不会被Velocity解释为一个变量或者一个属性的,因为2.5或者2或者5都不是一个VTL标识符。

转义:

假如需要在文本中最后显示"$foo"这个字符组成的字符串本身,而非变量$foo的值,可以使用转义字符\。假定$foo已经定义过了,那么:

  1. #set($foo="bar")
  2. $foo 输出 bar
  3. \$foo 输出 $foo
  4. \\$foo 输出 \bar
  5. \\\$foo 输出 \$foo

假如$foo没有定义,那么:

  1. $foo 输出 $foo
  2. 注意这里不是输出空字符串,因为没有键入默参符 "$!foo"
  3. \$foo 输出 \$foo
  4. \\$foo 输出 \\$foo
  5. \\\$foo 输出 \\\$foo

04.大小写

Velocity是大小写敏感的。但是为了减少使用者的错误,某些地方存在特殊情况。比如$foo.bar,首先会被检查是否能匹配$foo.getbar(),再被检查是否能匹配$foo.getBar()。而$foo.Bar,首先会被检查是否能匹配$foo.getBar(),再被检查是否能匹配$foo.getbar()。正如在《02.—引用》所提到的那样,属性不会被解析为对象的实例变量,即$foo.bar不表示foo这个java对象的bar属性。

05.#set指令

引用可以让模板设计者生成动态内容,而指令允许设计者真正的负责页面的展现和内容。

指令是以#开头,后面紧跟一个关键字,比如ifelseforeach等。而这个关键字,是可以被放在{}中间的。这是规范的写法,但是可能不好看。

#set

#set指令用于向一个变量或者属性赋值。其格式为 #set($foo="bar")或者#set($foo.bar=$test)

LHS必须是一个变量或者属性,RHS必须是以下7种类型之一:变量、属性、字符串字面值、数字字面值、方法、ArrayList、Map。比如:

  1. #set( $test = $test2 + 3)
  2. #set( $test.a = "abc")
  3. #set( $test.b = 1)
  4. #set( $test.c = $test.a)
  5. #set( $test.d = $test.calc("1+1"))
  6. #set( $test.e = ["123", "abc", $test.c]) 访问时使用$test.e.get(0)就可以获得值"123"
  7. #set( $test.f = {"name":"car", "color":"red"}) 访问时使用$test.f.get("color")就可以获得值"red"

默认情况下,RHS是null的话,将不会对LHS进行赋值,LHS会保持原来的值。所以一个引用一旦被创建,一般是无法移除的。但是可以修改Velocity的某个配置,改变这一现象。在#foreach里进行判断时,要特别注意

在使用#set时,字符串的字面值如果放在双引号里,将会被解析。比如#set( $test.a = "abc"), 那么$test.a的值就是abc。而#set( $test.b = "$test.a")等价于#set( $test.b=$test.a )。但是#set( $test.b = '$test.a') 的结果却是把$test.a这个字符串字面值赋值给$test.b

这里的单引号和双引号类似js中的作用但比js更强 ""双引号的等式将会被直接解析,而单引号内的任何值or等式运算都将被解析成字面字符串

当遇到需要输出大量的字符串字面值,而不是输出它们的实际值的情况时,可以使用#literal() ... #end来包围VTL Code。

06.if指令

  1. #if ($foo < 10)
  2. Go North
  3. #elseif ($foo == 10)
  4. Go East
  5. #else
  6. Go West#end 不能漏掉

#if指令用于判断某个条件是否为true。以上面的代码为例,当$foo < 10true时,将会显示"Go North"该字符串。

有时会看到这样一种写法:#if($foo) ... #end。这里充当判断条件的是一个变量$foo,它在两种情况下是true:一是$foo作为布尔值(Boolean)并且其值为true,二是$foo是一个非布尔值并且值不为null

关系运算

Velocity的关系运算中有一个==。它和Java中的==不太一样。java中==严格判断两个对象是否是同一个对象(内存地址是否是同一个,是否是同一个类的对象),而不是判断它们的值是否相同(两个String的字面值是否相同要使用equals()方法)。而Velocity中的==可以直接比较数值,字符串以及其他对象,如果==两侧的对象是不同的类的对象,在比较前会自动toString()转换后再比较。

其实这里的==和JS里面的==是一样的

逻辑运算

Velocity的逻辑运算有AND、OR、NOT三个。表现在语法上是&&、||、!这三个符号。下面是几个示例:

  1. #if($foo && $bar<10)
  2. ...
  3. #end
  4. #if($foo || $bar)
  5. ...
  6. #end
  7. #if(! $foo )
  8. ...
  9. #end

需要注意的是,&&和||是有短路效应的。一旦前一个就可以判断真假,后面的条件不再进行判断。另外,要特别注意$!foo!$foo的区别。
! $foo 表示NOT
$!foo 表示默参符

07. #foreach指令

example a:

  1. #foreach( $elem in $allElems)
  2. $elem</br>
  3. #end

上面这个例子中,$allElems可以是一个Vector,or一个HashTable,or一个数组。
在Velocity中,有一个变量$velocityCount用作循环计数,初始值是1。这个变量的名字和初始值是在velocity.properties文件里配置的。

example b:

  1. #set($nums=[-2 .. 2])
  2. #foreach($i in $nums)
  3. $i
  4. #end

example c:

  1. #foreach($key in $allElems.keySet())
  2. Key:$key ---> Value:$allElems.get($key)<br>
  3. #end
  4. 其中,allElems是一个HashTable

08.引入指令和#Stop指令

#Include#Parse都是用于将本地文件引入当前文件的指令,而且被引入的文件必须位于TEMPLATE_ROOT。这两者之间有一些区别。

#Include 指令

#Include引入的文件,其内容不会被Velocity引擎解析,所以这些文件应该是静态模板,即不含有VTL的模板。使用#Include()指令时,参数是被双引号括起来的文件名或者是表示文件名的变量。如果有多个文件,以逗号隔开即可。比如#Include("a.gif", "b.html", $file)

#Parse 指令

#Parse用来在当前模板中引入并执行另一个(本地的)模板——可以是静态的,也可以是动态的——并把结果嵌入到当前位置。#Parse()指令的参数,可以是一个双引号括起来的文件名,也可以是一个变量,但是它不能接受多个参数。

#Parse引入的文件仍然可以使用#Parse指令。在velocity.properties文件中有一个属性directive.parse.max.depth,默认值是10,它指定了#Parse嵌套的最大层次。既然#Parse嵌套是允许的,#Parse递归也是允许的。

假如a.vm #Parse b.vm,那么a.vm中定义的变量$v,在b.vm中可以随便使用。如果b.vm也定义了$v,那么b.vm中用到的将会是自己的$v,而不是a.vm中的$v

#Stop指令

#Stop指令会停止模板引擎的执行,并返回。这在debug中比较有用。

09.宏 #macro

code a:

  1. #macro( d )
  2. <tr><td></td><tr>
  3. #end

这段代码定义了一个宏,名字为d,没有参数。下面是使用这个宏的代码:

achieve a:

  1. #d()

Velocity在遇到#d()的时候,会用<tr><td></td></tr>替代上面的#d()这一行。

宏的参数:

宏也可以带参数,而且是任意多个参数。不过,宏定义时有几个参数,宏调用时就要提供同样数目的参数。
ps:即形参数量等于实参数量,类似PHP ,JAVA 等中古语言

code b:

  1. #macro( d $name)
  2. <tr><td>$name</td></tr>
  3. #end

achieve b:

  1. #d("name1")

宏的参数可以是以下VTL元素中的任意一种:引用、字符串字面值、数值字面值、整数范围(比如[1 .. 10][$start .. $end])、数组、布尔值true或者false。

宏的参数可以是方法,那么下面这个例子,需要特别注意:

example:

  1. #macro(test $a)
  2. $a $a $a
  3. #end
  4. #test($foo.bar())

上面这个例子中,$foo.bar()将会被调用3次,而不是一次。

内联的宏

当宏是在一个Velocity模板中定义时,这个宏(是inline的)只能被该模板使用,同一个网站下的其他模板是不能用的。如果是在一个Velocity宏模板库中定义的宏,就可以被任何同一网站下的模板使用。

和宏有关的一些Velocity属性

velocimacro.library——用逗号分隔的一组文件名,是Velocity宏模板库。默认值是VM_global_library.vm

velocimacro.permissions.allow.inline——宏是否可以在一个普通模板中定义。默认值是false

velocimacro.permissions.allow.inline.to.replace.global——是否允许模板中的宏覆盖library中的宏。默认值是false

velocimacro.permissions.allow.inline.local.scope——一个在普通模板中定义的宏,是否允许其他模板使用。默认是false

velocimacro.context.localscope——在一个宏里通过#set()修改了context,此修改是否仅仅对这个宏自身,而不是永久性修改了context。默认值是false

velocimacro.library.autoreload——Velocity宏模板库修改之后,是否自动重新加载。默认值是false。debug时可以设置为true,发布时设置为false

其他一些注意点

宏必须在第一次使用它之前定义。当#Parse()一个模板文件时,尤其要注意这一点。

10.指令的转义

引用的转义使用"\",指令的转义也是使用"\"。但是,指令的转义要比引用的转义复杂很多。
example:

  1. #if($foo)
  2. Go!
  3. #end
  4. $footrue,输出Go!;否则不输出任何内容。

使用转义:

  1. \#if( $foo )
  2. Go!
  3. \#end

此时,"\"和"#"结合,从而破坏了原本的#if(),而foofoo是true还是false,结果都是

  1. #
  2. if( $foo )
  3. Go!
  4. #end

这类似于使用了#literal() ... #end指令。

如果使用两个"\":

  1. \\#if( $foo )
  2. Go!
  3. \\#end

首先,#if前面的两个假定$footrue,那么会输出"Go!",并且是紧跟在刚才的"\"后面(因为没有换号符)。#if($foo)#end之间的内容,都会输出,包括#end前面的两个"\",它们会被解析为一个"\"。如果$foofalse,仅仅输出一个"\",而"Go!"以及后面下一行的"\"不会输出。"\"结合,变为一个"\"输出。然后#if($foo)正常处理。
ps:这里的意思是两个斜杠捺\即为输出一个为\的字符串

那么三个\\\这种情况呢?可以自己动手试一试


添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注