在之前的章节里,我们对Velocity引用进行了简要的介绍,甚至在示例里使用了部分引用。然而,再三延缓讨论引用的目的是为了让学习曲线更平缓、易学。既然你已经了解Velocity是什么和了解一个基本的应用程序是如何工作的,那么就让我们开始仔细学习Velocity引用吧!只有相当熟悉地掌握了Velocity引用,才能为你学习和掌握Velocity提供强大的动力!
引用类型
正如我们在前面章节里暗示的一样,Velocity引用具有引用一个存储在上下文中的简单字符串值的能力,引用事实上可以引用任何一种存储在上下文的类型。注意,很重要的一点是,我们所使用对象必须引用的是一个真正的JAVA对象——即,对象实例必须满足是java.lang.Object类的实例这个条件。除了简单类型和纯粹的静态类外,其余均不能放置到上下文中。
我们先前曾经提及过Velocity引用的名称,去掉其前缀就和上下文里存储的引用对象的关键字相同了。然而,这并非完全是这样的——多半情况下可以这样说,但并不是全部。虽然所有有效的Velocity引用名称包含了用于上下文关键字的名称,但除了需要字符串化版本的上下文对象外,还需要更多其他途径才能满足要求。这里总共有三种不同类型的Velocity引用,每一种有其自己的命名约定。下面我们将讨论这三种类型,分别为:变量、方法和属性。
变量
我们目前所遇到的所有Velocity引用都属于变量类型。这种类型的引用符合字符串化(stringified)JAVA对象要求,它的值可以通过它的toString()方法来输出。由于JAVA类层次的根类是JAVA对象类,它提供了toString()实现,所以任何Velocity上下文对象都可以作为变量引用。当然,对象类提供的toString()实现仅仅返回一个相伴类的名字字符串和对象哈希代码的十六进制表示(这个十六进制显示内容除了用于调试外,没有什么用处)。一个更实际一点的Velocity变量引用示例引用了一个JAVA数字包装类(比如Integer或Float)的实例。
JAVA的包装类很可能是Velocity变量的引用目标,因此不限于内置JAVA类的实例。只要是和Integer、Float一样的JAVA类,开发者就可以重载类的toString()实现,目的是为了提供更多有用的字符串表现。为了更深入的理解,让我们看一个调用了几个变量引用的示例。
我们将事先定义几个定制类,并主要关注它的toString()方法。Listings 7.1 和 7.2定义了两个价值不高(普通)的类,每一个都呈现了一个单一的字符串值,并存储为一个public的数据成员;使用public的成员数据不是好主意,我们这样做的原因是为了强调Velocity通过toString()方法获取它的字符串化(stringified)显示比通过直接访问成员数据要好。通过下面列出来的代码你可以发现,他们之间的差异在于重载类(Listings7.2)重载了toString()方法,非重载类(Listings 7.1)的toString()方法是从对象继承的。
public class NoOverride
{
public final String value = "toString() not overridden";
}
Listing 7.1 The NoOverride class definition没有重载的类定义.
public class Override
{
public final String value = “toString() overridden”;
public String toString()
{
return (value);
}
}
Listing 7.2 The Override class definition.有重载的类定义
示例的模板实现代码见Listing 7.3。除示范用户自定义类的变量引用之外,模板还包含了变量引用打算映射到普通的JAVA对象,一个JAVA Integer对象和一个Java Float对象。当这个模板使用Listing 7.4列出的代码进行处理时,输出为Listing 7.5所示。注意,除上下文组装以外,Listing 7.4的代码几乎和Listing 6.4一样,它们仅仅在类和模板(文件和变量)的名称有区别。另外在这里除了有一个重载需要处理。
## A variable reference for a generic Java Object
Value of generic Object is $genericObject
## A variable reference for an Integer object
Value of Integer object is $integerObject
## A variable reference for a Float object
Value of Float object is $floatObject
## A variable reference for a custom object with no toString() override
Value of user object with default toString() is $userNoOverride
## A variable reference for a custom object with a toString() override
Value of user object with overridden toString() is $userOverride
Listing 7.3 The template for the variable reference example.
import java.io.StringWriter;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.*;
public class VarRef
{
public static void main( String[] args )
{
// Initialize template engine
try
{
Velocity.init();
}
catch( Exception x )
{
System.err.println( "Failed to initialize Velocity: " + x );
System.exit( 1 );
}
// Obtain a template
Template varTemplate = null;
try
{
varTemplate = Velocity.getTemplate( "VarRef.vm" );
}
catch( ResourceNotFoundException rnfX )
{
System.err.println( "Template not found: " + rnfX );
System.exit( 1 );
}
catch( ParseErrorException peX )
{
System.err.println( "Failed to parse template: " + peX );
System.exit( 1 );
}
catch( Exception x )
{
System.err.println( "Failed to initialize template: " + x );
System.exit( 1 );
}
// Create context
VelocityContext context = new VelocityContext();
// Populate context
context.put( "genericObject", new Object() );
context.put( "integerObject", new Integer( 10 ) );
context.put( "floatObject", new Float( 3.1415 ) );
context.put( "userNoOverride", new NoOverride() );
context.put( "userOverride", new Override() );
// Merge template and context
StringWriter writer = new StringWriter();
try
{
varTemplate.merge( context, writer );
}
catch( ResourceNotFoundException rnfX )
{
System.err.println( "Template not found on merge: " + rnfX );
System.exit( 1 );
}
catch( ParseErrorException peX )
{
System.err.println( "Failed to parse template on merge: " +peX );
System.exit( 1 );
}
catch( MethodInvocationException miX )
{
System.err.println( "Application method exception: " + miX );
System.exit( 1 );
}
catch( Exception x )
{
System.err.println( "Failed to merge template: " + x );
System.exit( 1 );
}
// Render merged content
System.out.println( writer.toString() );
}
}
Listing 7.4 The driver code for the variable reference example.
Value of generic Object is java.lang.Object@cdedfd
Value of Integer object is 10
Value of Float object is 3.1415
Value of user object with default toString() is NoOverride@bf2d5e
Value of user object with overridden toString() is toString()overridden
Listing 7.5 Output from the variable reference example application.
在Listing 7.5中你可以看到,Velocity引用符合我们的非重载类,它没有重载toString()方法,输出结果和普通JAVA对象相似。这是因为Velocity使用toString()来获取字符串化(stringified)值,并且不用关心它下面的成员数据。非重载类使用的toString()方法事实是由Java对象类带过来的,这样的输出没有什么特别的地方。在使用重载类的情况下,变量引用的结果是由重载的toString()方法提供的。Java Integer 和 Float类实现了他们自己的toString()方法,在某种意义上表现为他们各自的类型,相应的引用将产生其对应的输出结果。
回到引用名称上来,快速比较一下上下文组装代码(Listing 7.4)的关键字和模板引用名称(Listing 7.3),你可以发现他们之间存在以下关系:
genericObject => $genericObject
integerObject => $integerObject
floatObject => $floatObject
userNoOverride => $userNoOverride
userOverride => $userOverride
非常清楚,除了$前缀外,关键字和引用名称是一样的,但他们还是有区别的(这个关系不是不致的。)在变量引用的情况下,我们之前声明的关于在关键字和引用名称之间关系是完全正确的。更确切地说,变量引用名称就是上下文关键字加上$前缀(或$!:用于quiet notation<静态符号>的情况下,随后将进行讨论)。
正好,用Velocity的行话讲,通过变量引用的有效上下文关键字名称必须遵照VTL标识符语法。正确的VTL标识符是一个由英文字母开始,后跟英文字母、数据字符、连字号(-)或下划线(_)的字符串组成(英文字母可以是小写的az或大写的A-Z,数字可以为0-9)。Velocity变量引用就是在VTL标识符前加前缀$(或$!)。
方法
和变量引用一样,Velocity方法引用名称包含了一个具有$前缀的VTL标识符。然而,在方法引用的情况下,它还需要一个VTL方法体。VTL方法体由一个VTL标识符、一对圆括号和可选参数(多个参数间用逗号分隔)组成。VTL标识符和VTL方法体之间用点(.)连接,示例如下:
$date.changeTo( 2003, "March", 2 )
$car.newColor("Blue" )
$document.print()
引用名称的前半部分(在点[.]之前)和那些变量引用的语法是相同的。也就是说,引用名称(包含VTL标识符)的上半部分对应上下文对象的上下文关键字。Velocity方法引用的剩余部分提供所引用JAVA对象方法实现的名称和参数列表。为了让Velocity访问引用方法,这个方法必须声明为public,同时必须是public类的成员。
Velocity的方法引用和许多Velocity的变量引用是同样的。方法引用被放置在一个模板动作被调用的地方。直觉判断,在那些较早出现在模板里的引用之后以及在那些较迟出现在模板里的引用之前,Velocity引擎将处理任何一个已经给予的Velocity引用。同样地,Velocity引用的赋值应该被考虑为一个连续的处理过程,随着动作被较早的引用调用,它将会影响其随后的引用的行为。
当方法引用相应的JAVA方法返回一个值时,模板处理收益(proceed)从某种意义上和变量引用处理相似。当JAVA方法执行完成后,通过调用对象的toString()方法返回Velocity字符串化(stringifies)值,该返回值比JAVA对象返回的原始值要好,Velocity首先把值转换为适当的JAVA封装类(比如:整形值转换为Integer,布尔值转换为Boolean等等)。在任何情况下,在最终模板输出时结果字符串将替换方法引用。
为了对Velocity方法引用的用法有更好的体验,让我们来看一个示例。Listing 7.6提供了一个Java类,它展示的是一个汽车的主要技术规范。这个技术规范包括制造、模型、颜色和生产年份。方法用于定义这些规范的值。另外,方法还提供了显示这些定义值的方法。
public class Auto
{
public void defineType( String make, String model )
{
this.make = make;
this.model = model;
}
public void defineColor( String color )
{
this.color = color;
}
public void defineYear( Integer year )
{
this.year = year;
}
public String printSpecs()
{
return (color + " " + year + " " + make + " " + model);
}
private String make;
private String model;
private String color;
private Integer year;
}
Listing 7.6 An automobile specification class.
下面是组装一个上下文必须要的代码,它包含一个Auto类的实例:
context.put( "car", new Auto() );
Auto的实例可以被模板(Listing 7.7)访问和操作。这个模板使用Velocity方法引用通过对象的defineType()、define-Color()和 defineYear()方法来定义汽车的技术规范。然后,对象的printSpecs()方法又一次通过方法引用被调用,目的是为了获取一个包含汽车技术规范定义的字符串。这个字符串被增加到模板输出,详见Listing 7.8。
## Define the make and model of the automobile
Defining automobile type...$car.defineType( "Chevrolet", "Cavalier")
## Define the color of the automobile
Defining automobile color...$car.defineColor( "Blue" )
## Define the year of the automobile
Defining automobile year...$car.defineYear( 1997 )
## Display the automobile specifications
$car.printSpecs()
Listing 7.7 汽车技术规范模板
Defining automobile type...
Defining automobile color...
Defining automobile year...
Blue 1997 Chevrolet Cavalier
Listing 7.8 汽车技术规范模板处理结果
一个Velocity方法引用的重要方面就是到目前为止,我们已经巧妙地避开了参数类型。任何一个插入到模板里面或从模板里提取(extract)的值都会当成字符串来处理。我们先前曾经提及,任何一个插入到模板的非字符串值(通过变量引用或方法引用返回的值),都是使用其相应对象的toString()方法来字符串化(stringified)。然而,事情并非这样简单,当提及从模板里提取(extract)值时(当Velocity处理方法引用的参数时便会发生这种情况)就不同了。
作为模板语言,和高级编程语言不同,Velocity模板语法不提供数据类型机制。同样地,Velocity模板引擎期望所有的方法引用参数都为字符串。字符串型值通过引用字符串(用单引号如‘stringValue’,或双引号如“stringValue”)在模板里被明确指定。另外,Velocity也支持隐式(implicit)字符串规范,那就是如果整形值在JAVA Integer类型支持的值域范围内时,就不必引用这样的值。但这种支持不能扩展到除Integer类型以外的范围(比如long类型)或其他数字类型(比如float, double),如果需要正确处理这些参数值,则需要提供能接受字符串值并进行自定义类型转换的Java方法。未来的Velocity版本可能会增强数字类型的支持。
属性
Velocity属性引用本质上讲是一个Velocity方法引用的扩展。利用反射(introspection《自省/反省》),Velocity提供了一个交替接口,它是一个以set或get开始命名的公开Java方法。这个交替接口允许模板象访问普通Java对象属性一样访问这种方法,目的是让模板代码更整洁更易读。Velocity方法引用可能需要一个模板设计者使用一些像$obj.getValue()的方法访问部分数据,而属性引用只需使用$obj.Value的方法就可以访问同样的数据。
Velocity属性机制的功能比我们说的要更进一步。它除了提供交替接口功能外,还提供了一个扩展功能(功能为:it does so in a case-insensitive manner.)。如果在模板里遇到$obj.Value,Velocity首先会查找名叫getValue()的方法,如果没有找到,Velocity会继续查找名叫getvalue()的方法。换句话说,属性引用最初将被当作方法引用$obj.getValue()等同对待,如果Velocity不能匹配引用到适当的Java方法,它将考虑把属性引用当作方法引用$obj.getvalue()等同对待,并且再次尝试查找相应的Java方法。同样地,属性引用$obj.value将第一个被当作$obj.getvalue()或$obj.getValue()方法对待。
除上面这种情况外,属性机制支持对象的get()方法实现交替接口(In addition to eliminating issues of case, the property mechanism supports the alternate interface for objects implementing get() methods),比如下面这些原型定义:
Object get( Object key );
String get( String key );
第一个原型和Java的Map类定义是一样的,它明确指示Velocity的属性引用适用于任何实现了Map接口的类的对象,假设那些对象用真实的实体字符串关键字来进行操作。第二个原型很可能提供了更好的、适用于用户自定义实现的原型,来自模板和返回到模板的东西要不是明确的,要不就是已经暗中处理成字符串了的。
正如我们做过的Velocity变量或Velocity方法引用,我们现在介绍一个简单的示例来解释如何使用属性引用。在最后一段,我们将再次使用Auto类来介绍;然而,printSpecs()方法现在被三个新的方法替换,每一个方法都返回printSpecs()的一部分。新方法的名字叫getType(), get-Color(), 和getYear(),他们将返回汽车制造厂、型号、颜色和出厂年份。
public class Auto
{
public void defineType( String make, String model )
{
this.make = make;
this.model = model;
}
public void defineColor( String color )
{
this.color = color;
}
public void defineYear( Integer year )
{
this.year = year;
}
public String getType()
{
return (make + " " + model);
}
public String getColor()
{
return (color);
}
public Integer getYear()
{
return (year);
}
private String make;
private String model;
private String color;
private Integer year;
}
Listing 7.9 Our revised Auto class with support for the property reference interface.
属性引用示例的模板实现见Listing 7.10。模板的上半部分定义了汽车技术规范的值,它和Listing 7.7的方法引用示例是相同的。模板的下半部分示范了三种访问Auto对象属性的技术。第一种:采用大写字母开头的属性名称;第二种:采用小写字母开头的属性名称;第三种:属性值是通过等价的方法引用而获得。不管采用哪种技术,其输出都是相同,见Listing 7.11。Velocity的属性引用简单提供了一个交替接口给模板设计者,在幕后,同样的Java方法以同样的方式被调用。
## Define the make and model of the automobile
Defining automobile type...$car.defineType( "Chevrolet", "Cavalier")
## Define the color of the automobile
Defining automobile color...$car.defineColor( "Blue" )
## Define the year of the automobile
Defining automobile year...$car.defineYear( 1997 )
## Display the automobile specifications (upper case properties)
$car.Color $car.Year $car.Type
## Display the automobile specifications (lower case properties)
$car.color $car.year $car.type
## Display the automobile specifications (method references)
$car.getColor() $car.getYear() $car.getType()
Listing 7.10 A template demonstrating the use of property references.
Defining automobile type...
Defining automobile color...
Defining automobile year...
Blue 1997 Chevrolet Cavalier
Blue 1997 Chevrolet Cavalier
Blue 1997 Chevrolet Cavalier
Listing 7.11 Results from processing the automobile specification template.
为了替代上面的分离的属性访问方法,现在我们假定Auto类实现了一个key-based 的、接近于属性访问的方法。如果实现在某种意义上和Listing 7.12的代码相似,Velocity的反射(introspection反省、自省)机制将为属性引用(在上一个示例里定义的)提供后续支持。在$car.Type的处理过程中,Velocity将检索一个有能力来get( “Type” )的Java方法。如果找到这样的一个方法,(仅有在Velocity的反射不能找到getType()和gettype()时)该方法将被调用。$car.Color 和 $car.Year将有同样的方法来实现。在get()实现里的字符串比较将通过equalsIgnoreCase()方法来实现,属性引用$car.type、 $car.color和 $car.year也将保持有效。
public String get( String item )
{
if ( item.equalsIgnoreCase( "type" ) )
{
return (make + " " + model);
}
else
if ( item.equalsIgnoreCase( "color" ) )
{
return (color);
}
else
if ( item.equalsIgnoreCase( "year" ) )
{
return (year.toString());
}
else
{
return ("");
}
}
Listing 7.12 A key-based implementation of property access for the Auto class.
正式引用符号(Formal Reference Notation)
迄今为止,在我们的示例和讨论中,我们一直限制使用Velocity的简化的或正式的引用符号(notation)。Velocity也提供了一个正式的标记符(notation),主要是为了改善模板易读性。不管你偏爱什么,理解正式标记符(notation)很重要,因为为了避免模板不易读、含糊不清,它有时是必须的。比如,假使你需要从http://www.my.site/pageN.html产生一连串的URLs,pageN.html里的N代表动态产生的页面数(如: page2.html, page99.html),可以尝试使用简化标记符(shorthand notation)的Velocity变量引用来产生页面数,模板代码和下面这段相似http://www.my.site/page$number.html。这段模板代码在“$number.html”上有问题,它看起来和Velocity属性引用(一个具有$number 对象的HTML属性)相象。Velocity仅仅假定了这么一个引用,难道就没有别的出路来实现这个意图吗?如果引用是不正确的假设,那么引用有可能正确解决,也可能不能解决。在任意一种情况下,最后结果或许可能是不需要的输出。
如何我们使用Velocity方法引用代替上面的方法来生成URL的页面数,运行的时候也会出现相似的问题,其模板代码看起来象http://www.my.site/page$number.getNext().html这个一样。在这里,有一个使用$number.getNext()方法引用返回对象的HTML属性请求,其最后结果或许还是不需要的输出。如果Velocity属性引用被用在方法引用的地方,则模板代码变成了http://www.my.site/page$number.next.html,既然$number.next和$number.get-Next()相当,模板处理的结果将和方法引用一样,遭遇再次失败。
轻松解决这个问题的方法是使用Velocity的正式引用符号代替Velocity的简化符号(shorthand notation),正式符号非常适用于解决上面提到的这种含糊不清的模板处理问题。正式标记符清楚地界定了引用,给Velocity提供了足够的信息去实现你的意图,在代码不明确的情况下(比如上面介绍的)也能正确执行。引用用一对大括号({})和一个放在大括号前面的$前缀进行界定,其余部分和引用语法相同。我们所讨论的这个URL模板代码将用正式符号进行重写,如下所示:
http://www.my.site/page${number}.html
http://www.my.site/page${number.getNext()}.html
http://www.my.site/page${number.next}.html
安静记号符(Quiet Notation)
除了标准的$前缀外,Velocity引用可以使用前缀“$!”。$!提供了Velocity想要当作quiet notation(安静符号)进行引用的东西。安静记号符(Quiet notation)影响了模板引擎在处理引用找不到对应上下文对象时的处理方式(manner)。在很多情况下,如果不能在上下文里定位适当的对象或方法,Velocity会把引用当作标准文本对待,直接以静态内容的方式进行输出。这表面在Listing 7.14里的前三行,模板的处理结果(使用了一个空的上下文)见Listing 7.13。和这个相反,在Listing 7.14里的最后三行文本使用了安静记号符(quiet notation)。Velocity的安静记号符(quiet notation)使用空白字符串(比如“”)有效替换未解决的引用,否则他们会被当成静态内容直接输出。
## Standard variable reference notation
This variable reference is loud...$object.
## Standard method reference notation
This method reference is loud...$object.getValue().
## Standard property reference notation
This property reference is loud...$object.Value.
## Quiet variable reference notation
This variable reference is quiet...$!object.
## Quiet method reference notation
This method reference is quiet...$!object.getValue().
## Quiet property reference notation
This property reference is quiet...$!object.Value.
Listing 7.13 A template illustrating the use of both standard and quiet reference notation.
This variable reference is loud...$object.
This method reference is loud...$object.getValue().
This property reference is loud...$object.Value.
This variable reference is quiet....
This method reference is quiet....
This property reference is quiet....
Listing 7.14 The template output demonstrating the difference between standard and quiet reference notation.
虽然示例使用的是简化引用符号。当与Velocity的正式引用符(formal reference notation)结合时,安静记号符(quiet notation)和正式引用符一样是相等的、有效的。利用正式引用符实现,在Listing 7.13的模板应该调整为Listing 7.15所示的模板代码。假定在缺乏有效上下文实体的情况下,这两个模板的输出结果是相同的。需要注意的一点是:当结合安静(quiet)和正式(formal)记号符(notation)时,“!”字符应该放在大括号(curly braces)的外面。
## Standard variable reference notation
This variable reference is loud...${object}.
## Standard method reference notation
This method reference is loud...${object.getValue()}.
## Standard property reference notation
This property reference is loud...${object.Value}.
## Quiet variable reference notation
This variable reference is quiet...$!{object}.
## Quiet method reference notation
This method reference is quiet...$!{object.getValue()}.
## Quiet property reference notation
This property reference is quiet...$!{object.Value}.
Listing 7.15 The template from Listing 7.13 implemented using formal reference notation.
忽略(Escape转义)引用
当我们在之前的章节里讨论安静记号符(quiet notation)时,示范了一个模板内容虽然看起来象是Velocity引用,但不与上下文的对象或方法绑定,在未使用(employ雇用)安静记号符(quiet notation)的地方仅被当作静态上下文处理。
当模板本身需要看起来和变量引用相同的静态内容时,就会经常用到这个方法。
比如,许多脚本语言的变量定义也是在变量前加上前缀$,如果任务是使用这样的语言来书写模板代码。了解这门语言的变量可能会减少大量的单调工作。
然而,需要注意的是,这个技术没有保证。万一Velocity不顾上下文的内容,强制对一个具有$前缀的实体当作引用进行处理,一旦在VTL定义语法中不允许使用冒号,解析结果就是错误的。
因此,或许一些特殊情况,比如不想让${foo:bar}被Velocity处理,原样输出。也就是说,你如何明确阻止Velocity把某些看起来象引用的广本当作引用来处理?最简单的解决方法就是忽略(转义)这个引用。这通过在引用的前缀($)之前插入一个反斜线(")来完成。
为了更进一步阐明本身的问题,让我们对Listing 7.16的模板进行仔细考虑。模板的第一部分对Velocity的变量引用命名进行了简要描述。包含在$email和$name里的静态内容将照原样显示在最终输出里。如果上下文缺乏相应的对象来对应这些字符串引用,Velocity将产生你想要的输出。然而,模板的第二部分提供了一个需要产生动态内容的联系信息,而且,这个动态部分也使用了$name和$email,但在这种情况下,他们将被真实的Velocity引用替换,而不是静态内容。
## Description of names used for standard, shorthand variable references Velocity variable references are built upon the VTL identifiers used to key the associated objects in the context. For example, an object keyed with the string 'email' would be referenced from the template with $email. Likewise, an object keyed with the string ‘name’ would be referenced with $name.
## Contact information For more on this topic, contact $name at $email.
Listing 7.16 A template that illustrates the need to escape references.
如果上下文包含了姓名和email关键字,那么Velocity将用相应的对象值替换所有的$name和$email。另一方面,如果上下文不包含姓名和email关键字,那么Velocity不替换任何一个$name和$email。非常清楚,两个动作都不能提供想要得到的结果。解决方法是忽略(转义)$name和$email,无论什么情况下,Velocity将把他们当作简单静态内容对待。模板代码见Listing 7.17。
假定上下文包含字符串“John”(对应关键字为“name”)和doe@my.site(对应关键字“email”),模板处理结果见Listing 7.18。
## Description of names used for standard, shorthand variable references Velocity variable references are built upon the VTL identifiers used to key the associated objects in the context. For example, an object keyed with the string 'email' would be referenced from the template with "$email. Likewise, an object keyed with the string 'name' would be referenced with "$name.
## Contact information For more on this topic, contact $name at $email.
Listing 7.17 A template using escaped references.
Velocity variable references are built upon the VTL identifiers used to key the associated objects in the context. For example, an object keyed with the string ‘email’ would be referenced from the template with $email. Likewise, an object keyed with the string 'name' would be referenced with $name. For more on this topic, contact John at doe@my.site.
Listing 7.18 Output produced by the template in Listing 7.17.
事实上,反斜线提供了Velocity引用的转义。当你在引用之前,实际上想要一个文本的反斜线该如何做?答案仍然是增加更多的反斜线。反斜线的数量决定于需要多少文本性的反斜线和是否忽略引用本身。Velocity按从左向右的顺序对反斜线进行处理,每一对反斜线减少成一个反斜线字符。如果在处理结束后,剩下了奇数数量的反斜线,则引用被忽略;否则,将对引用进行处理。也就是说,如果是一个奇数数量的反斜线,那么结果是引用被忽略;如果是一个偶数数量的反斜线,那么结果是引用被它定义在上下文里的值替换。Listing 7.19提供了一个模板,在进里面的引用前增加了不同数量的反斜线,isting 7.20显示了Listing 7.19处理后的结果。
##有相应上下文对象情况下的引用转义
$object.getValue()
"$object.getValue()
""$object.getValue()
"""$object.getValue()
""""$object.getValue()
"""""$object.getValue()
""""""$object.getValue()
Listing 7.19 A template showing the use of varying numbers of backslash reference escapes.
formal
$object.getValue()
"formal
"$object.getValue()
""formal
""$object.getValue()
"""formal
Listing 7.20 Listing 7.19模板的输出结果
你需要明白的是使用反斜线将会使Velocity变得更复杂。当你的模板在使用一个引用时,如果存在反斜线,那么你要非常小心,一不小心就会出错,结果找不到对应的上下文对象,有一些仅仅看起来象引用,但它只被用作文字输出。1.3.1版的行为依赖于是否存在一个偶数或奇数数量的反斜线。当反斜线数量为偶数时,所有的反斜线都当作文字处理,不进行任何转义。相反,如果反斜线数量为奇数时,每一对(从左边开始)反斜线被当作转义处理,结果是输出一个反斜线。最后的反斜线被当成文本处理。详见Listings 7.21 和7.22。
##在没有相关上下文对象情况下的引用转义
$noobject.getValue()
"$noobject.getValue()
""$noobject.getValue()
"""$noobject.getValue()
""""$noobject.getValue()
"""""$noobject.getValue()
""""""$noobject.getValue()
Listing 7.21使用了反斜线数量不断增加的模板
没有使用上下文组合对象。
$noobject.getValue()
"$noobject.getValue()
""$noobject.getValue()
""$noobject.getValue()
""""$noobject.getValue()
"""$noobject.getValue()
""""""$noobject.getValue()
Listing 7.22 Listing 7.21模板的输出结果
本章小节和下章介绍
在本章里,我们深入研究了Velocity的引用,包括变量、方法和属性引用。我们也测试了Velocity的正式和静态标记法(formal and quiet notations)以及引用转义(escaping)。在这一点上,你应该已经掌握了足够的知识,足以让你正确使用Velocity引用进行开发工作。在下一章里,在已经掌握Velocity引用的基础上,我们将通过学习指令,并使用指令进一步扩展模板的功能。Velocity的指令把模板设计带到另外一个水平,它允许模板设计者访问那些在许多编程语言里才具有的控制结构集。