Velocity空间

快速构建JAVA应用
随笔 - 11, 文章 - 15, 评论 - 5, 引用 - 0
数据加载中……

CHAPTER 9 Velocimacros

 

在之前的章节里,我们讨论了所有Velocity的指令,包括#macro指令。然而,我们只是简要介绍了#macro指令。在这里,我们将重新讨论#macro指令(也可以称为Velocimacros)。我们将讨论参数传递、宏库、运行时配置和其他与Velocimacros相关的主题。

参数传递Argument Passing

当我们在Chapter 8里介绍#macro指令时,我们展示了一个简单的Hello World示例。The template used for that example defines an inline Velocimacro that outputs Hello world each time it is invoked. This macro is defined so that no arguments are allowed in the macro invocation. Note that the #macro directive itself always requires at least one argument, the first of which specifies the name used to invoke the macro. However, the invocation of a macro takes zero or more arguments, depending on its specification.(示例里使用的模板定义了一个用于输出Hello world的内联Velocimacro。这个宏定义为没有参数。注意,#macro指令最小需要一个参数,第一个参数用于指定调用宏的名称。然而,宏的invocation获得0个或更多参数,完全依赖于它的规范)

为了允许把参数传递到Velocimacro,你只需要一个为每一个需要传递的参数提供了名称的#macro指令,这些参数用空格进行分隔。每一个引用名可以作为Velocity变量引用用于宏的代码块。和#foreach#if关系指令一样,代码块用#end指令终止。为了演示这个例子,我们把Listing 8.40示例的Velocimacro部分进行改动,具体改动为:把简单的输出hello改为输出个人详细信息。这个新改进的宏见Listing 9.1。在处理的时候,这个模板将在字符串“Hello Zaphod!”之后新增一个字符串“Hello Arthur!”

## Define inline macro for this template

#macro( sayHiTo $who )

Hello $who!

#end

## Invoke the macro using normal directive syntax

#sayHiTo( "Arthur" )

#sayHiTo( "Zaphod" )

Listing 9.1 A template demonstrating the #macro directive with support for argument passing.

为了能够成功调用Velocimacroinvocation的参数个数必须和宏定义指定的参数个数匹配。比如,如果Listing 9.1里的宏被作为#sayHiTo() #say-HiTo(“Zaphod” “Beeblebrox”)进行调用,则invocation将被忽略。由此得出结论,Velocimacro不支持默认参数;也不支持重载。同样地,在Velocimacro 故意改变参数个数的情况下,这儿并没有什么独特的解决方法。在很多类似情况下,为宏指定唯一名称是必需的。

迄今为止,我们仅仅演示了使用字符串作为输入参数。其实,Velocimacro也支持integer、布尔类型、值域操作符、数组列表和Velocity引用。Listing 9.2展示了使用以上所有类型作为输入参数的模板。第一个Velocimacro需要使用字符串、integer和布尔类型。第二个需要使用值域操作符和数组列表。最后一个需要使用Velocity引用。注意,这些宏定义和invocation的参数列表都是用空格来定界的,和许多通用编程语言使用逗号进行分隔一样。宏只是简单输出提供的参数值,见Listing 9.3

## Define Velocimacros

## (string literal - integer literal - boolean)

#macro( sib $string $int $bool )

The string is $string.

The integer is $int.

The boolean is $bool.

#end

## (range - array list)

#macro( ra $range $arrayList )

#foreach ( $val in $range ) $val #end

#foreach ( $val in $arrayList ) $val #end

#end

## (Velocity reference)

#macro( r $vref )

The reference correspond to $vref

#end

## Invoke Velocimacros

#sib( "Hello" 42 true )

#ra( [-9..-1] ["favorite", "color"] )

#set( $color = "Blue. No! Yellow!" )

#r( $color )

Listing 9.2 A template demonstrating the #macro directive used with various argument types.

The string is Hello.

The integer is 42.

The boolean is true.

-9 -8 -7 -6 -5 -4 -3 -2 -1

favorite color

The reference correspond to Blue. No! Yellow!

Listing 9.3 Results from processing the template in Listing 9.2.

我们在前面的示例里已经演示了Velocimacro直接支持的多种类型参数。下面我们将讨论用Velocity引用作为Velocimacro的输入参数。把引用作为宏的输入参数并不仅限于变量引用,还可以使用Velocity方法引用和Velocity属性引用。虽然使用这些引用作为输入参数是可行的,但在使用中仍然要小心。正确使用这些引用的诀窍就是深入理解他们是通过名字进行参数传递的(pass by name)。That is, the reference is not evaluated until after it is received by the Velocimacro。为了阐明这些概念,让我们来考虑FromFive类(见Listing 9.4),这个类简单初始化一个int属性,赋值为5,每一次通过类的getNext()方法请求时,将消耗一点这个值。

public class FromFive

{

public FromFive()

{

nextValue = 5;

}

public Integer getNext()

{

return (new Integer( nextValue-- ));

}

public String toString()

{

return (String.valueOf( nextValue ));

}

private int nextValue;

}

Listing 9.4 The FromFive class, which generates a sequence of decreasing integer values.

接着,假定我们的应用程序使用三个FromFive类的实例(对应关键字为字符串为method-RefpropRefvarRef)来维护Velocity上下文组装。如果这个应用程序需要处理的模板为Listing 9.5所示,其输出结果为Listing 9.6As the output demonstrates, the value associated with the $ref reference in the countdown Velocimacro changes each time it is evaluated for the cases of method and property reference input. This is due to the fact that in these two cases $ref stores the names $methodRef.getNext() and $propRef.next, respectively, rather than the values those references evaluate to. In effect, $ref simply becomes an alias for the provided method or property reference. Although not as obvious, the same is in fact true for the variable reference $varRef; the associated output differs only due to the fact that FromFive’s toString() method does not decrement the value of an object’s nextValue property. If toString() were modified to behave in the same manner as getNext(), then passing $varRef to the countDown Velocimacro would also result in decrementing output.

## Evaluate provided reference six times.

#macro( countDown $ref )

$ref.. $ref.. $ref.. $ref.. $ref.. $ref

#end

## Call countDown with a method reference

#countDown( $methodRef.getNext() )

## Call countDown with a property reference

#countDown( $propRef.next )

## Call countDown with a variable reference

#countDown( $varRef )

Listing 9.5 A template demonstrating the use of all three types of Velocity references with the #macro directive.

5.. 4.. 3.. 2.. 1.. 0

5.. 4.. 3.. 2.. 1.. 0

5.. 5.. 5.. 5.. 5.. 5

Listing 9.6 Results from processing the template in Listing 9.5.

如果必需要通过值类型传递(pass by value)方式来传递Velocity引用的判断结果值,最容易的解决方法是#set指令来捕获这个独立的变量引用值,之后传递那个引用。让我们来看一下Listing 9.7里的模板,这个模板的内联宏定义和Listing 9.5相同。然而,我们改变invocation来模仿值类型传递(pass-by-value)行为。注意,这个新的模板只通过#set指令途径来仿效值类型传递。在下面的封装,调用仍然是名字传递(pass-by-name)。

## Evaluate provided reference six times.

#macro( countDown $ref )

$ref.. $ref.. $ref.. $ref.. $ref.. $ref

#end

## Call countDown with the value of a method reference

#set( $methodValue = $methodRef.getNext() )

#countDown( $methodValue )

## Call countDown with the value of a property reference

#set( $propValue = $propRef.next )

#countDown( $propValue )

## Call countDown with the value of a variable reference

#set( $varValue = $varRef )

#countDown( $varValue )

Listing 9.7 A template demonstrating emulated pass-by-value behavior with a #macro directive.

Inline vs. Library Macros

迄今为止,我们的Velocimacro示例都集中在内联定义上。虽然内联定义可以用于很多场合,但其固有的特性限制了内联定义只能用于代码再使用。内联Velocimacro的作用域仅限于其定义的模板文件,更明确一点,只限于具有宏定义的那部分模板文件。因此,其他模板文件不能访问这样的宏。如果尝试通过#include#parse指令把一个模板中的Velocimacro定义应用到其他作用域中时,将会失败。

#include指令导入的只是静态内容,因此#macro指令将失去其特定的意义。#parse指令把包含进来的文本当作普通模板代码来处理。与此相反,当模板第一次被模板引擎解析时,Velocimacro调用被终止,在任何#parse指令导入外部的Velocimacro定义时。(The #include directive imports only static content, so #macro directives would lose any special meaning. The #parse directive does process the included text as normal template code, but it does so at runtime. In contrast, Velocimacro calls are determined when the template is first parsed by the template engine, well before any #parse directives have a chance to import external Velocimacro definitions)。

因此,如何通过多模板共享Velocimacro,从而避免单调重复的拷贝和粘贴?答案就是Velocity对宏库的支持。这个特性允许你创建多宏库,创建方法是应用程序通过Velocimacro属性的变量进行注册来创建。一旦这样的库被注册,那么应用程序处理的任何模板都可以库中调用这个Velocimacro。我们将在本章稍后讨论Velocimacro属性,并在Chapter 10 “Taking Control of Velocity”)对Velocity的属性系统进行讨论。现在,我们只需了解,通过属性系统,包含#macro指令的文件可以作为Velocimacro库,让所有的模板访问。

(如果你的Velocimacro库需求是适度的,那么)根本就不必为Velocity的属性系统烦扰。默认情况下,模板引擎会把任何一个文件假定为位于应用程序目录下的VM_global_library.vm文件,并将其解释为宏库。比如,如果我们的模板(来自Listing 9.5)被分割成两个Listings 9.8Listings9.9的文件,输出结果仍是一样的(见Listing 9.6)。仅仅需要把Velocimacro库命名成VM_global_library.vm即可。

## Evaluate provided reference six times.

#macro( countDown $ref )

$ref.. $ref.. $ref.. $ref.. $ref.. $ref

#end

Listing 9.8 A macro library containing the Velocimacro originally defined in Listing 9.5. If Velocity defaults are assumed, this file must be named VM_global_library.vm.

## Call countDown with the value of a method reference

#countDown( $methodRef.getNext() )

## Call countDown with the value of a property reference

#countDown( $propRef.next )

## Call countDown with the value of a variable reference

#countDown( $varRef )

Listing 9.9 The template from Listing 9.5 after moving its Velocimacro to a macro library.

Velocimacro Properties

Velocity提供了许多配置属性,用于调整Velocimacros的行为。我们将在Chapter 10讨论这些属性的具体功能,现在,我们只了解其名称、说明和默认设置。

 

velocimacro.library

我们曾经在前面章节里提到过库属性。这个属性用于定义应用程序Velocimacros库里文件的名称,该文件名称是相对于当前模板路径配置的。如果有多个文件需要包含到库里,那么在这些文件名之间用逗号分隔。库属性的默认值是VM_global_library.vm

 

velocimacro.permissions.allow.inline

permissions.allow.inline属性用于定义是否允许内联(inlineVelocimacro定义。也就是说,是否可以把Velocimacro宏定义在一个非库模板文件里。如果为false,内联Velocimacro定义将导致日志警告信息,并且该定义将被模板引擎忽略。默认值为true

velocimacro.permissions.allow.inline.to.replace.global

permissions.allow.inline.to.replace.global属性用于定义是否允许内联(inline) Velocimacro用相同的名称去重载一个Velocimacro库。当然,只有在permissions.allow.inline属性为true里才有意义,否则,内联定义就不能通过。如果permissions. allow.inline.to.replace.global属性为true,和内联Velocimacro 同名的Velocimacro库将被内联Velocimacro替代。如果为falseVelocimacro库将受到保护,不管有意还是无意的内联Velocimacro库都不能替代Velocimacro库。默认值为false

 

velocimacro.permissions.allow.inline.local.scope

permissions.allow.inline.local.scope属性用于定义是否允许模板为Velocimacro提供私有(private)命名空间。当这个属性为true时,就允许提供私有命名空间,同时内联Velocimacro定义可用于定义模板。无论何时Velocimacro定义被请求时,允许私有命名空间也将导致模板的命名空间首先被搜索。最后一个特性是允许本地Velocimacro定义重载任何其他在外部模板定义的定义。默认值是false

velocimacro.context.localscope

context.localscope属性用于定义#set指令影响在Velocimacro 中使用Velocity上下文的方式。当属性值为true时,Velocimacro将有效接收它自己本地的上下文。调用者的对象上下文关键字对Velocimacro来说不可见,转而变成使用来自Velocimacro内部的上下文,而且不复制回转给调用者。与此相反,当属性值为false时,将把调用者的上下文放到一个Velocimacro可达到的、可更改的地方。也就是说Velocimacro将使用调用者的上下文。默认值为false

velocimacro.library.autoreload

library.autoreload属性用于定义当库中的宏被调用时,是否允许自动重新加载这个修改过的Velocimacro库。如果属性值为true,每一次调用Velocimacro库都将检查相应库的修改信息,如果检查发现这个库在最后一次加载后被修改过,Velocity将自动重新加载这个库。如果属性值为falseVelocimacro库一旦加载后就不再进行修改信息检测。默认值为false。该重加载功能主要用于程序测试和调试。如果你需要允许这个属性,那么很可能你也需要禁止资源加载缓存,我们将在下一章讨论属性控制这个缓存。

velocimacro.messages.on

messages.on属性用于指定是否让模板引擎产生附加的关于Velocimacros的日志信息。当这个属性的值为true时,就需要产生附加信息;为false时,将不产生附加信息;默认值为true

嵌套和递归

迄今为止,本章所有的Velocimacro示例都仅限于不使用嵌套和递归的情况。嵌套,最简单的情况就是在Velocimacro里调用另外一个Velocimacro,这是在实际代码开发中使用得最频繁的一种。递归,是一种特殊类型的嵌套,它是在Velocimacro里调用自身,但这种情况并不太常见。幸运的是,Velocimacro对嵌套和递归都提供了支持。Listing 9.10里演示了Velocimacro使用嵌套和递归的例子。Velocimacro重复调用它自己,调用次数由它的参数$depth指定。writeABC宏通过调用getAgetBC Velocimacros演示了Velocimacro嵌套。Listing 9.11显示了输出结果。

## A recursive Velocimacro

#macro( recurs $depth )

Entering at level $depth

#set( $depth = $depth - 1 )

#if ( $depth > 0 )

#recurs( $depth )

#end

#set( $depth = $depth + 1 )

Leaving from level $depth

#end

## Nesting of Velocimacros

#macro( getA ) A #end

#macro( getB ) B #end

#macro( getC ) C #end

#macro( getBC )

#getB()#getC()

#end

#macro( writeABC )

#getA()#getBC()

#end

#recurs( 3 )

#writeABC()

Listing 9.10 A template demonstrating the use of nesting and recursion of Velocimacros.

Entering at level 3

Entering at level 2

Entering at level 1

Leaving from level 1

Leaving from level 2

Leaving from level 3

A B C

Listing 9.11 Results from processing the template in Listing 9.10.

本章小节和下章介绍

在这一章里,我们对#macro指令(或叫Velocimacro展开了深入的讨论。讨论覆盖了参数传递、宏库、嵌套和递归、以及Velocimacro属性。这在一点上,你应该已经对Velocity的上下文、模板语言以及引用和指令有了较好的理解。你现在应该已经可以开发模板了。然而,仍有许多Velocity功能需要你去了解。这就我们下一章将要讨论的内容。

posted on 2008-10-19 21:28 KINGWEE 阅读(781) 评论(0)  编辑  收藏 所属分类: Velocity


只有注册用户登录后才能发表评论。


网站导航: