- 浏览: 36546 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
isy:
很明了的一个小例子,感谢分享!
Spring AOP介绍 -
lyglcheng:
3q
jquery校验框架 -
bruceyu:
Java调用Ant API用法 -
chenhua_1984:
很好!下载了!
jquery校验框架 -
PointOnLine:
以前我也搞个这个dwr,不过好像不能调用重载的方法。
DWR
velocity中文开发指南
Contents
概述和入门 资源 Velocity怎样工作
基本的格式
使用Singleton还是不使用
Singleton模式 Separate Instance
上下文
基础原则 对使用#foreach()遍历对象的支持 上下文链 模版创建的对象 其他问题
在Servlets里使用
Servlet编程 部署Deployment
在一般的应用中使用
Velocity Helper对象 异常 更多的细节
应用的属性 EventCartridge 和 Velocity的配置 配置日志系统
使用现有的Log4jUsing 一个自定义日志的例子
配置资源加载器(模版加载器)
资源加载器 配置示例 插拔式的资源管理器和资源缓存
模版的编码和国际化 Velocity 和 XML FAQ (Frequently Asked Questions) 总结Summary 附录1:部署示例ServletAppendix
Jakarta Tomcat Caucho Technology's Resin BEA
概述
Velocity是一个基于Java技术的模版引擎,是一个简单但强大的使你能轻松的创建和合成具有一定格式,用于展现数据的文档的工具。在本指南中,我们希望能使你对使用Velocity能有一个比较全面的认识。主要关注两个主要的使用Velocity的领域:
基于servlet的web应用。servlet-based WWW development 普通的应用。general application use
你将会发现其实两者之间的差别很小。当使用我们提供的VelocityServlet作为基于servlet的web应用的基础,加上我们提供的一些工具类,构建一个web应用会及其的简单。
入门Getting Started
即使在Velocity官方网站还是在Velocity的文档中,都能找到下面的信息,但是为了完整这里还是再提下。要使Velocity在你的电脑上开始工作是很简单的事情。注意下面提到的文件夹都是从你的Velocity发布文件的根目录开始的。
得到Velocity的发布文件。 如果你还没有安装Jakarta Ant这个构建工具,请安装好它。需要Ant是为了构建Velocity,而不是为了使用Velocity。 切换到发布文件的build文件夹下。 输入 ant <build target>,其中,<build target>将会是下面之一:
jar 在bin文件夹下构建完整的Velocity jar文件。这个jar文件会命名为Velocity-X.jar,其中X是当前Velocity的版本号。注意这个jar文件没有包括Velocity必需的库文件。如果你使用了这个任务,你需要从JAakarta Commons上得到Collections组件,并且将该jar文件添加到你的CLASSPATH中(或者放到WEB-INF/lib)。如果你希望使用日志或其他功能,你也需要把相关的jar文件添加到CLASSPATH或者WEB-INF/lib中。为了方便,你可以直接使用jar-dep任务来将需要的jar都打包到一起。 jar-dep 在bin文件夹下构建完整的Velocity jar文件,包括了Velocity所有依赖的库文件。 jar-core 在bin文件夹下构建一个小型的Velocity jar文件,该文件叫做velocity-core-X.jar。该jar中只包括了Velocity最核心的功能,不包括例子和工具,比如Anakia,Texen,VelocityServlet等。关于该jar文件的依赖组件和jar任务一样,也没有包括。 jar-util 在bin文件夹中构建Velocity的工具jar,该文件叫做velocity-util-X.jar。该jar中只包含了Velocity提供的工具,Anakia,Texen和WebMacro模版转换工具。关于该jar文件的依赖组件和jar任务一样,也没有包括。 jar-servlet 在bin文件夹中构建Velocity的工具jia,该文件叫做velocity-servlet-X.jar。该jar包含了servlet相关的工具代码。关于该jar文件的依赖组件和jar任务一样,也没有包括。 jar-J2EE 构建一个完整的jar文件。和jar任务相似,该jar文件包含了J2EE应用所需要的所有的组建。现在,多增加的文件就只有org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader。和其他任务一样,该jar文件也是放在bin文件夹下的,叫做velocity-j2ee-X.jar。注意,如果你希望使用该任务,你必须将j2ee.jar拷贝到build/lib文件夹中。我们没有在任何发布文件中提供该jar文件。在http://java.sun.com上能找到他。关于该jar文件的依赖组件和jar任务一样,也没有包括。 jar-J2EE-dep build a complete jar with J2EE support and includes logging support from the Jakarta Avalon Logkit and regexp support fromt the Jakarta ORO package. See the notes on the jar-dep target, above. examples 构建在examples文件夹中的例子代码。该任务同样会构建论坛例子项目。 forumdemo 构建在examples/forumdemo文件夹中的web应用实例。 docs 在docs文件夹中使用Anakia XML转换工具构建文档。允许你在构建中使用Velocity模版作为样式表。注意,该任务需要把jakarta-site2项目放在jakarta-velocity发布文件夹中。关于更详细的内容请参看在build.xml文件中该任务的详细说明。 jar-src 将所有的Velocity代码放在一个jar里面,置于bin文件夹中。 javadocs 在doc/api文件夹中构建API文档。 test (after jar) will test Velocity against it's testbed suite of test routines help 列出所有可用的任务。
虽然不是必须的,但首先测试一下构建是一个不错的想法。使用test任务即可。 所有的事情都OK了。Velocity已经能开始工作了。将合适的jar文件置于你的类路径中,或者放在合适的位置(比如web-inf/lib) 我们建议你先运行下例子来感受下Velocity。通过输入ant examples来构建。
依赖包Dependencies
Velocity使用Java2 API。构建Velocity需要Java2 SDK。要运行Velocity,需要Java2 RTE。
Velocity为了完成一些通用的功能,需要一些额外的包。为了方便,他们都放在build/lib文件夹下,但是默认的构建任务并没有包含这些jar。如果你使用的是默认的任务构建Velocity,你必须要把这些jar文件放入你的classpath。
Jakarta Commons Collections - 必需的required. Jakarta Avalon Logkit - 可选的,但是很常用。如果需要使用Velocity默认的基于文件的日志功能。 Jakarta ORO - 可选的。如果要使用org.apache.velocity.convert.WebMacro 模版转换工具。optional. Needed when using the org.apache.velocity.convert.WebMacro template conversion utility. 资源Resources 下面有一些例子和资源。我们建议你能看看我们提供的例子,文档甚至源代码。下面是一些很好的资源。 用户和开发者的交流社区:通过mail-lists加入我们。 Mail-list资料库: http://www.mail-archive.com 。在搜索框中输入velocity来查看user和dev的文档。 源代码:在Velocity项目下的src/java文件夹中。 应用示例1:一个简单的用于演示怎样在一个应用程序中使用Velocity。 应用示例2:一个简单的用于演示怎样通过使用Velocity应用程序工具包来在应用程序中使用Velocity。 servlet例子:一个简单的用于演示怎样在servlet中使用Velocity的例子。 日志例子:一个简单的用于演示怎样自定义一个日志记录器并将它注册到velocity中来接收日志消息的例子。 XML例子:examples/xmlapp_example : a simple example showing how to use JDOM to read and access XML document data from within a Velocity template. It also includes a demonstration of a recursive Velocimacro that walks the document tree. event 例子:一个用来演示在Velocity1.1中出现的事件处理API的使用。 Anakia 应用:application : 一个用来演示怎样使用Velocity来为XML数据创建样式表。 论坛Web演示应用Forumdemo web app :一个基于servlet的论坛演示应用。 documentation : docs : all the generated documentation for the Velocity project in html API documentation : docs/api : the generated Javadoc documentation for the Velocity project 模版:大量的关于模版的示例,这些都是极好的VTL的例子。 上下文例子:两个关于怎样扩展上下文对象的例子,适用于高级用户。
上面所有引用到的文件夹都是基于发布文件的根目录的。 Velocity怎样工作
How Velocity Works 基本的格式'The Fundamental Pattern'
当你在一个应用程序中或者一个servlet中,你一般会按照下面的方式来做。When using Velocity in an application program or in a servlet (or anywhere, actually), you will generally do the following :
初始化Velocity:对于两种使用环境都适用------Singleton和separate rutime instance,并且该工作只需做一次。Initialize Velocity. This applies to both usage patterns for Velocity, the Singleton as well as the 'separate runtime instance' (see more on this below), and you only do this once. 创建一个上下文对象(后面将详细介绍)。Create a Context object (more on what that is later). 把你的数据对象放入上下文对象中。Add your data objects to the Context. 选择一个模版。Choose a template. 把模版和你的数据一起输出。'Merge' the template and your data to produce the ouput.
在代码中,通过类org.apache.velocity.app.Velocity使用Singleton模式,代码如下:In code, using the singleton pattern via the org.apache.velocity.app.Velocity class, this looks like import java.io.StringWriter;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.Template;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.MethodInvocationException;
Velocity.init();
VelocityContext context = new VelocityContext();
context.put( "name", new String("Velocity") );
Template template = null;
try
{
template = Velocity.getTemplate("mytemplate.vm");
}
catch( ResourceNotFoundException rnfe )
{
// couldn't find the template
}
catch( ParseErrorException pee )
{
// syntax error : problem parsing the template
}
catch( MethodInvocationException mie )
{
// something invoked in the template
// threw an exception
}
catch( Exception e )
{
}
StringWriter sw = new StringWriter();
template.merge( context, sw );
这就是基本的格式。很简单,不是吗?这基本上就是你使用Velocity来合成模版所需要的步骤。当然在实际的应用中你不会像这样书写代码,我们为servlet和普通应用提供了一些工具,让你能甚至比上面的代码更容易的使用Velocity。在本指南的后面,我们将介绍在servlet和普通应用中使用Velocity的更多细节,和那些不错的工具。在每一种情况下,当然,上面的运行的顺序是不变的,或者在幕后是这样的。That's the basic pattern. It is very simple, isn't it? This is generally what happens when you use Velocity to render a template. You probably won't be writing code exactly like this - we provide a few tools to help make it even easier than this for both servlet and application programmers. Later on in this guide, we will talk about using Velocity in both servlets as well as general applications, and we discuss the tools we provide to make things easier. In each case, though, the above sequence is what is happening either explicitly, or behind the scenes. 使用singleton或者不...To Singleton Or Not To Singleton... 在Velocity1.2之后,开发人员就有了两种使用Velocity引擎的选择------单例模式或者非单例模式。两中选择都使用的是相同的Velocity代码,让Velocity更简单的融入到你的Java应用中。As of Velocity 1.2 and later, developers now have two options for using the Velocity engine, the singleton model and the separate instance model. The same core Velocity code is used for both approaches, which are provided to make Velocity easier to integrate into your Java application.
单例模式Singleton Model
这是一种老的模式,即在JVM中只存在一个Velocity引擎的实例,整个应用共享这一个。这种方式对于配置文件的定位和共享资源都是很方便的。比如,该模式极其适合使用在Servlet2.2+标准下的web应用中------每个web应用都拥有其唯一的一个Velocity实例,允许web应用的servlet共享诸如模版,日志记录器等资源。这个单例的实例可以通过org.apache.velocity.app.Velocity.app.Velocity类创建。下面是一个使用单例模式的例子:This is the legacy pattern, where there is only one instance of the Velocity engine in the JVM (or web application, depending) that is shared by all. This is very convenient as it allows localized configuration and sharing of resources. For example, this is a very appropriate model for use in a Servlet 2.2+ compliant web application as each web application can have it's own instance of Velocity, allowing that web application's servlet to share resources like templates, a logger, etc. The singleton is accessable via the org.apache.velocity.app.Velocity class, and and example of use : import org.apache.velocity.app.Velocity;
import org.apache.velocity.Template;
...
/*Configure the engine - as an example, we are usingourselves as the logger - see logging examples*/
Velocity.setProperty( Velocity.RUNTIME_LOG_LOGSYSTEM, this);
/*now initialize the engine*/
Velocity.init();
...
Template t = Velocity.getTemplate("foo.vm");
请注意,在org.apache.velocity.servlet.VelocityServlet 基础类(一个用于使创建servlet更简单的工具类)中,使用的就是单例模式。通过继承该类创建使用Velocity的servlet是最简单和最方便的方式,当然,这不是必须的。serbletPlease note that the Singleton model is used in the org.apache.velocity.servlet.VelocityServlet base class, a utility class provided with the distribution to make writing servlets easier. While extending this class is the most common and convenient way to write servlets using Velocity, you are free to not use this class if you needs require something different.
分离的实例Separate Instance
在1.2中的新出现的分离的实例模式允许你在一个JVM中创建,配置和使用许多Velocity引擎的实例。如果你想在同一个应用中为不同的Velocity引擎配置不同的资源,模版,日志记录器等,这种方式特别有用。通过使用org.apache.velocity.app.VelocityEngine来创建分离实例模式。下面是一个例子,功能同上一个例子:New in version 1.2, the separate instance allows you to create, configure and use as many instances of Velocity as you wish in the same JVM (or web application.) This is useful when you wish to support separate configurations, such as template directories, loggers, etc in the same application. To use separate instances, use the org.apache.velocity.app.VelocityEngine class. An example, which parallels the above singleton example, looks like :
import org.apache.velocity.app.[VelocityEngine];
import org.apache.velocity.Template;
...
/*create a new instance of the engine*/
VelocityEngine ve = new VelocityEngine();
/*configure the engine. In this case, we are usingourselves as a logger (see logging examples..)*/
ve.setProperty( VelocityEngine.RUNTIME_LOG_LOGSYSTEM, this);
/*initialize the engine*/
ve.init();
...
Template t = ve.getTemplate("foo.vm");
如你所见,这是很简单并且很直接的。除了一些简单的形式上的改变,使用单例模式还是分离模式对于你应用的高层次的架构和模版是没有影响的。As you can see, this is very simple and straightforward. Except for some simple syntax changes, using Velocity as a singleton or as separate instances requires no changes to the high-level structure of your application or templates.
作为一个程序员,需要强调的是使用org.apache.velocity.app.Velocity类来应用单例模式,使用org.apache.velocity.app.VelocityEngine类来应用非单例模式(分离实例模式)。As a programmer, the classes you should use to interact with the Velocity internals are the org.apache.velocity.app.Velocity class if using the singleton model, or org.apache.velocity.app.VelocityEngine if using the non-singleton model ('separate instance').
另外,不要使用在org.apache.velocity.runtime包中的Runtime,RuntimeConstants,RuntimeSingleton或者RuntimeInstance类。这些类是用于框架内部的,并且在不断的变化中。正如上面提到的,你应该使用在org.apache.vleocity.app包中的类------Velocity和VelocityEngine。At no time should an application use the internal Runtime, RuntimeConstants, RuntimeSingleton or RuntimeInstance classes in the org.apache.velocity.runtime package, as these are intended for internal use only and may change over time. As mentioned above, the classes you should use are located in the org.apache.velocity.app package, and are the Velocity and VelocityEngine classes. If anything is missing or needed from those classes, do not hesitate to suggest changes - these classes are intended for the application developer. 上下文对象The Context 基础The Basics
Velocity是以上下文概念为中心的,并且这是一种普通的在系统的各个部分中传递数据容器的技术。可以这样认为,上下文是在Java层(程序员关注的)和模版层(设计人员关注的)之间数据的负载者。程序员会将所有应用需要的对象,无论什么类型都聚集在一起,放入上下文中。对于页面设计者,在上下文中的对象,和它们的属性及方法都通过叫做引用的模版元素来调用。一般来说,你需要和设计者一起设计在页面上需要显示的数据,这些会成为设计者在页面中需要使用的API。所以,在这个阶段的时间的投入和仔细的分析是很值得的。The concept of the 'context' is central to Velocity, and is a common technique for moving a container of data around between parts of a system. The idea is that the context is a 'carrier' of data between the Java layer (or you the programmer) and the template layer ( or the designer ). You as the programmer will gather objects of various types, whatever your application calls for, and place them in the context. To the designer, these objects, and their methods and properties, will become accessable via template elements called references. Generally, you will work with the designer to determine the data needs for the application. In a sense, this will become an 'API' as you produce a data set for the designer to access in the template. Therefore, in this phase of the development process it is worth devoting some time and careful analysis.
同时,Velocity允许你创建自己的上下文对象来支持特别的要求和技术(比如连接一个LDAP的上下文)。一个很好的扩展点是继承VelocityContext类。While Velocity allows you to create your own context classes to support special needs and techniques (like a context that accesses an LDAP server directly, for example), a good basic implementation class called VelocityContext is provided for you as part of the distribution.
VelocityContext满足一般的需要,并且我们强烈建议你使用它。只有在例外或者高级的情况下,你需要继承或者创建你自己的上下文对象。VelocityContext is suitable for all general purpose needs, and we strongly recommended that you use it. Only in exceptional and advanced cases will you need to extend or create your own context implementation.
使用VelocityContext和使用普通的Java Hashtable对象差不多。你需要的最主要的两个方法是:Using VelocityContext is as simple as using a normal Java Hashtable class. While the interface contains other useful methods, the two main methods you will use are public Object put(String key, Object value);
public Object get(String key);
请注意这和Hashtable相似,里面的值必须继承自java.lang.Object,并且不是null。基础类型,比如int或者float必须要用适当的包装类包装才能使用。Please note that like a Hashtable, the value must be derived from java.lang.Object, and must not be null. Fundamental types like int or float must be wrapped in the appropriate wrapper classes.
这就是context基础的操作。需要了解更多的信息 ,请参考发布文件中的API文档。That's really all there is to basic context operations. For more information, see the API documentation included in the distribution.
使用*#foreach()来遍历对象{*}Support for Iterative Objects for #foreach()
作为一个程序员,对于放入上下文中的对象你有很大的自由度。在最大范围的自由度里需要遵守一些规则,所以,理解Velocity支持什么,就不会有错误会发生了。Velocity支持几种适合在VTL使用#foreach()指示符的集合类型对象。As a programmer, you have great freedom in the objects that you put into the context. But as with most freedoms, this one comes with a little bit of responsibility, so understand what Velocity supports, and any issues that may arise. Velocity supports serveral types of collection types suitable for use in the VTL #foreach() directive.
Object []正规的对象数组,在这里不需要再多说了。Velocity在框架内部用一个实现了Iterator接口的类来包装你的数组,具体的包装细节作为一个程序员或者模版设计者不需要过多的关注。Regular object array, not much needs to be said here. Velocity will internally wrap your array in a class that provides an Iterator interface, but that shouldn't concern you as the programmer, or the template author. java.util.Collection Velocity会使用iterator()方法来得到一个Iterator对象来执行遍历操作。所以,你需要使你的对象实现Collection接口,以使iterator()方法返回一个正常的Iterator对象。Velocity will use the iterator() method to get an Iterator to use in the loop, so if you are implementing a Collection interface on your object, please ensure that iterator() returns a working Iterator. java.util.Map这里,Velocity依靠values()方法来得到一个Collection接口,然后再调用iterator()方法来得到用来遍历的Iterator对象。Here, Velocity depends upon the values() method of the interface to get a Collection interface, on which iterator() is called to retrieve an Iterator for the loop. java.util.Iterator 小心使用:这是最近才支持的,不过最好是作为临时的使用,主要是因为Iterator的不可复位性决定的。如果一个Iterator直接被放入上下文,并且使用了多于一个的#foreach来遍历,那么第二个#foreach就会失败,因为Iterator并没有复位。USE WITH CAUTION : This is currently supported only provisionally - the issue of concern is the 'non-resettablity' of the Iterator. If a 'naked' Iterator is placed into the context, and used in more than one #foreach(), subsequent #foreach() blocks after the first will fail, as the Iterator doesn't reset. java.util.Enumeration 小心使用:和Iterator相似,这也是最近才支持的用于临时性的。原因也是Enumeration的不可复位性决定的。同样,如果一个Enumeration直接被放入上下文,并且使用了多于一个的#foreach来遍历,那么第二个#foreach就会失败,因为Enumeration并没有复位。USE WITH CAUTION : Like java.util.Iterator, this is currently supported only provisionally - the issue of concern is the 'non-resettablity' of the Enumeration. If a 'naked' Enumeration is placed into the context, and used in more than one #foreach(), subsequent #foreach() blocks after the first will fail, as the Enumeration doesn't reset.
我们建议,只有在必须的时候,才将Iterator 和Enumeration放入上下文中。In the case of the Iterator and Enumeration, it is recommended that they are placed in the context only when it cannot be avoided, and you should let Velocity find the appropriate reusable iterative interface when that is sufficient and possible.
尽管可能有充足理由直接在Context中使用Iterator(比如通过JDBC得到的大的数据集),但如果能够避免,最好还是使用其他的代替。这里直接使用是指象下面代码这样:There are good reasons to use the java.util.Iterator interface directly (large data sets via JDBC, for example), but if it can be avoided, it might be better to use something else. By 'directly' , we meant doing something like: Vector v = new Vector();
v.addElement("Hello");
v.addElement("There");
context.put("words", v.iterator() );
在上面的例子中,Iterator对象自己是放入了Context。作为代替,只需象这样:where the Iterator itself is placed into the context. Instead, if you simply did: context.put("words", v );
这样一切都能正常运行:Velocity将会发现Vector实现了Collection,并且会找到iterator()方法,并且在每一次调用#foreach之前刷新Iterator对象。如果使用的直接是Iterator对象,那么一旦velocity在其上使用了一次#foreach,Velocity没有办法再重新得到一个新的Iterator。这将导致所有在#foreach中使用该引用输出为空。then all would be fine: Velocity would figure out that Vector implement Collection (via List), and therefore will find the iterator() method, and use that to get a 'fresh' Iterator for its use each time it needs to. With just a plain Iterator (the first snippet above...), once velocity has used it in a #foreach(), Velocity has no way of getting a new one to use for the next #foreach() it is used in. The result is no output from any subsequent #foreach() blocks using that reference.
上面的介绍并不是说在Velocity中遍历对象是一件必须经过细致考虑的问题。不过,当你放入上下文中的是一个Iterator,确实需要小心谨慎。This above isn't meant to give the impression that iterating over collections in Velocity is something that requires great care and thought. Rather, the opposite is true, in general. Just be careful when you place an Iterator into the context.
上下文链Context Chaining
Velocity的上下文设计中的一个革命性的创新是加入了上下文链的概念,有时候也称为上下文包装。这种思想让你能将分散的上下文对象连接起来,并看作一个联合的上下文对象来使用。An innovative feature of Velocity's context design is the concept of context chaining. Also sometimes referred to as context wrapping, this advanced feature allows you to connect separate contexts together in a manner that makes it appear as one 'contiguous' context to the template.
最好使用一个例子来演示一下:This is best illustrated by an example : VelocityContext context1 = new VelocityContext();
context1.put("name","Velocity");
context1.put("project", "Jakarta");
context1.put("duplicate", "I am in context1");VelocityContext context2 = new VelocityContext( context1 );context2.put("lang", "Java" );
context2.put("duplicate", "I am in context2");template.merge( context2, writer );
在上面的例子中,我们建立连接context1的context2。这意味着在末拌种,你可以使用任何放在这两个上下文中的对象(但这时不能存在使用同样的key来放入对象)。如果这样做了(使用了重复的key来保存对象),在比较后面定义(外层)的上下文中的那个对象将有效。比如在上面的例子中,如果使用duplicate来引用,得到的将会是"I am in context2"In the code above, we have set up context2 such that it chains context1. This means that in the template, you can access any of the items that were put into either of the two VelocityContext objects, as long as there is no duplication of the keys used to add objects. If that is the case, as it is above for the key 'duplicate', the object stored in the nearest context in the chain will be available. In this example above, the object returned would be the string "I am in context2".
注意这种重复定义(或者叫做覆盖定义),对被覆盖了的对象没有任何害处或者改变。所以,在上面的例子中,字符串"I am in context1"仍然存在并状态良好,并且如果通过使用context1.get("duplicate")就能正确地得到。但在上面的例子中,在模版中使用$duplicate的引用会得到"I am in context2"的值,并且模版上的引用不可能得到"I am in context1"。Note that this duplication, or 'covering', of a context item does not in any way harm or alter the covered object. So in the example above, the string "I am in context1" is alive and well, still accessable via context1.get("duplicate"). But in the example above, the value of the reference '$duplicate' in the template would be 'I am in context2', and the template has no access to the covered string 'I am in context1'.
注意,如果你依赖从模版中向上下文添加一些在合成时需要检查的状态值,你需要小心。通过在模版中使用#set来改变上下文中的值得时候,只会影响最外层的上下文。所以,确保如果你不希望从模版中改变内层上下文的值得时候,不要丢弃外层的上下文。Note also that you have to be careful when you are relying on the template to add information to a context that you will examine later after the rendering. The changes to the context via #set() statements in a template will affect only the outer context. So make sure that you don't discard the outer context, expecting the data from the template to have been placed onto the inner one.
This feature has many uses, the most common so far is providing layered data access and toolsets.
如同前面所介绍的,Velocity的上下文机制也是能被扩展的,不过这超越了本指南的范围。如果你对这感兴趣,请参看在org.apache.velocity.context包中的类是怎样提供上下文对象并把它们联合起来的。同时,在examples/context_example目录下,也有一些例子演示了怎样扩展上下文,包括了使用数据库来作为上下文对象的例子(虽然这是个很愚蠢的想法)。As mentioned before, the Velocity context mechanism is also extendable, but beyond the current scope of this guide. If you are interested, please see the classes in the package org.apache.velocity.context to see how the provided contexts are put together. Futher, there are a few examples in the examples/context_example directory in the distribution which show alternate implementations, including [a goofy] one that uses a database as the backing storage.
请注意这些例子仅仅用于演示,而没有被验证实用价值。Please note that these examples are unsupported and are there for demonstration/educational purposes only.
在模版中创建的对象Objects Created in the Template
有两种情况下,Java代码需要处理在运行时从模版中创建的对象。There are two common situations where the Java code must deal with objects created at runtime in the template :
当一个模版的作者调用了一个放在上下文中的用Java编写的对象的方法的时候:When a template author calls a method of an object placed into the context by Java code. #set($myarr = "a","b","c" )
$foo.bar( $myarr )
当一个模版向上下文中添加了一些在模版合成之后,Java代码需要处理的对象时。When a template adds objects to the context, the Java code can access those objects after the merge process is complete. #set($myarr = "a","b","c" )
#set( $foo = 1 )
#set( $bar = "bar")
处理这些情况只需要了解一些事实:Dealing with these cases if very straighforward, as there are just a few things to know:
在VTL中当把一个范围操作[1..10]或者和对象数组["a","b"]放入上下文中是java.util.ArrayList对象。因此,当你设计的方法是需要接受一个数组的时候,请注意这一点。The VTL RangeOperator [ 1..10 ] and ObjectArray ["a","b"] are java.util.ArrayList objects when placed in the context or passed to methods. Therefore, your methods that are designed to accept arrays created in the template should be written with this in mind. 数字将作为Integer放入上下文中,字符串是String。Numbers will be Integers in the context, and strings will be, of course, Strings. 在方法调用的时候,Velocity也会接受原始值。比如通过#set放入上下文中的一个Integer也能调用setFoo(int i)方法。Velocity will properly 'narrow' args to method calls, so calling setFoo( int i ) with an int placed into the context via #set() will work fine.
关于上下文的其他问题Other Context Issues
One of the features provided by the VelocityContext (or any Context derived from AbstractContext) is node specific introspection caching. Generally, you as a the developer don't need to worry about this when using the VelocityContext as your context. However, there is currently one known usage pattern where you must be aware of this feature.
The VelocityContext will accumulate intropection information about the syntax nodes in a template as it visits those nodes. So, in the following situation:
You are iterating over the same template using the same VelocityContext object. Template caching is off. You request the Template from getTemplate() on each iteration.
It is possible that your VelocityContext will appear to 'leak' memory (it is really just gathering more introspection information.) What happens is that it accumulates template node introspection information for each template it visits, and as template caching is off, it appears to the VelocityContext that it is visiting a new template each time. Hence it gathers more introspection information and grows. It is highly recommended that you do one or more of the following :
Create a new VelocityContext for each excursion down through the template render process. This will prevent the accumulation of introspection cache data. For the case where you want to reuse the VelocityContext because it's populated with data or objects, you can simply wrap the populated VelocityContext in another, and the 'outer' one will accumulate the introspection information, which you will just discard. Ex. VelocityContext useThis = new VelocityContext( populatedVC ); This works because the outer context will store the introspection cache data, and get any requested data from the inner context (as it is empty.) Be careful though - if your template places data into the context and it's expected that it will be used in the subsequent iterations, you will need to do one of the other fixes, as any template #set() statements will be stored in the outermost context. See the discussion in Context chaining for more information. Turn on template caching. This will prevent the template from being re-parsed on each iteration, resulting the the VelocityContext being able to not only avoid adding to the introspection cache information, but be able to use it resulting in a performance improvement. Reuse the Template object for the duration of the loop iterations. Then you won't be forcing Velocity, if the cache is turned off, to reread and reparse the same template over and over, so the VelocityContext won't gather new introspection information each time. 在Servlets中使用Velocity
Using Velocity In Servlets
Servlet编程Servlet Programming
Velocity最常用的地方就是在Web应用中的Java Servlet编程了。有许多理由来解释为什么Velocity很适合这样工作,其中最主要的一个就是Velocity的强制性的将视图层和逻辑层分开了。有许多关于这方面的资料,包括这个。The most common use of Velocity is in the area of Java Servlet programming for the WWW. There are many reasons why Velocity is well suited for this task, one of the primary ones is Velocity's enforcement of the separation of the presentation (or view) layer from the code layer. There are many resources on this subject, including this.
把Velocity应用于servlet环境的基础技术是很简单的,所有你需要做的就是继承VelocityServlet基础类,并实现一个handleRequest()方法,这就是全部。The basic technique of using Velocity in a servlet environment is very simple. In a nutshell, all you must do is extend the provided VelocityServlet base class and implement a single method, handleRequest(). That's really all that is required to use Velocity in your servlet development.
对于Velocity1.1,有两个方法需要实现:As of Velocity 1.1, there are two handleRequest() methods :
public Template handleRequest( Context ) 这是比较老的一个方法,这个方法要求你返回一个合法的Template对象。如果该对象无效或者为null,则被认为是一个错误的状态,并使用error()错误处理方法来处理。如果需要,你可以覆盖error()方法。如果该方法返回null也是你希望的一个正确的返回值(比如你想重定向请求),建议你使用下面一个比较新的方法。This is the older of the two methods. This method requires that you return a valid Template object. If not valid, or null, this is considered an error condition, and will result in the error() error handling method being called. You may override the error() if you wish. If returning a null is something you expect to do (for example, you will want to redirect requests) it is recommended that you use the newer method, listed next.
public Template handleRequest( HttpServletRequest, HttpServletResponse, Context ) 这个方法比较新,在1.1中实现。这两者最大的区别在于在这个方法中,HttpServletReuqest和HttpServletResponse对象是作为参数传递给方法的。灵鸽一个不同的地方在于这个方法允许返回一个null来表明在该方法中所有的处理已经做完了,Velocity只能再调用requestCleanup()方法。这个方法在你需要重定向请求的时候特别有用。This is the newer of the two handleRequest() methods, implemented in version 1.1. The difference with this method is that the HttpServletRequest and HttpServletResponse objects are passed to you as arguments to the method, as well as in the Context. The other difference is that this method can return null to indicate that all processing has been handled by the method, and that Velocity should do nothing further than call requestCleanup(). This is extremely useful is you wish to redirect the request, for example. 同样,请参看Javadoc API文档来得到更多的信息。As always, please refer to the Javadoc API documentation for the definitive and latest notes.
下面这个例子和在发布文件中的SampleServlet.java文件(在example文件夹下)相似。The following code is similar to the SampleServlet.java class included in the distribution in the examples directory.
public class SampleServlet extends VelocityServlet
{
public Template handleRequest( HttpServletRequest request,
HttpServletResponse response,
Context context )
{String p1 = "Jakarta";
String p2 = "Velocity";Vector vec = new Vector();
vec.addElement( p1 );
vec.addElement( p2 );context.put("list", vec );Template template = null;try
{
template = getTemplate("sample.vm");
}
catch( ResourceNotFoundException rnfe )
{
// couldn't find the template
}
catch( ParseErrorException pee )
{
// syntax error : problem parsing the template
}
catch( Exception e )
{}return template;
}
}
很眼熟?创建上下文对象的错误处理在VelocityServlet中已经处理,并且merge()方法的调用也在Velocity基础类中处理了。总的来说,这和我们在介绍Velocity的使用的基本格式的时候是一致的。在该例子中,我们向context中放入了一些应用数据,然后返回了一个模版。Look familiar? With the exception of creating the context object, which is done for you by the VelocityServlet base class, and the merge() step which is also done for you by the VelocityServlet base class, it's identical to the basic code pattern we mentioned at the beginning of this guide. We take the context, add our application data, and return a template.
在传入方法(第一个方法)的Context对象中,保存了当前的HttpServletRequest和HttpServletResponse对象。他们使用VelocityServlet.REQUEST(value="req")和Velocity.RESPONSE(VALUE="RES")常量来引用。要在你的代码中使用这些对象,象下面这样做:The default Context object that is passed into the handleRequest() methods contains both the current HttpServletRequest and HttpServletResponse objects. They are placed in the context using the the constants VelocityServlet.REQUEST (value = 'req') and VelocityServlet.RESPONSE (value = 'res') respectively. To access and use these objects in your Java code : public Template handleRequest( Context context )
{
HttpServletRequest request = (HttpServletRequest) context.get( REQUEST );
HttpServletResponse response = (HttpServletResponse) context.get( RESPONSE );
...
或者在你的模版中这样引用:and in your templates: #set($name = $req.getParameter('name') )
如果需要更高级的使用,VelocityServlet基础类允许你覆盖请求处理的一些部分,下面的这些方法可能是一个扩展点:For more advanced uses, the VelocityServlet base class allows you to override parts of the handling of the request processing. The following methods may be overridden :
Properties loadConfiguration( ServletConfig ) 允许你覆盖普通的配置机制并且添加或者修改一些配置属性。覆盖这个方法用来覆盖或者参数化模版和日志的路径,该路径需要基于webapp root的绝对路径。Allows you to override the normal configuration mechanism and add or alter the configuation properties. This is useful for overriding or augmenting template and log paths, to set the absolute path into the webapp root at runtime.
Context createContext(HttpServletRequest, HttpServletResponse ) 与许你创建自己的Context对象。这在需要更高级的情况下使用,比如需要链化或者预加载一些工具或者数据。默认的该方法的实现简单的返回了一个包含了HttpServletRequest和HttpServletResponse的VelocityContext对象。请求和响应对象使用简单的包装类包装,以避免在一些servlet容器中自省时会发生的错误。但你可以象平常一样使用其中的request和response对象,或者从模版中引用其方法。只需要注意一点,就是他们不再是javax.servlet.XXXX类了,如果你需要使用类全名的时候,需要重视。Allows you to create the Context object yourself. This allows more advanced techniques, such as chaining or pre-loading with tools or data. The default implementation simply returns a VelocityContext object with the request and response objects placed inside. The request and response objects are wrapped in simple wrapper classes to avoid introspection problems that may occurr in some servlet container implementations. You can use the request and repsponse objects normally, accessing methods of either from the template. Just note that they aren't specifically javax.servlet.XXXX classes, if that is important to you.
void setContentType( HttpServletRequest,HttpServletResponse ) 允许你检查请求,并设置内容类型(content type)。默认的类型是根据Velocity.properties中规定的。如果没有在properties文件中设定,或者说在默认情况下,"text/html"将是内容类型。Allows you to examine the request and set the content type yourself, depending on the request or client. The default implementation sets the content type to be that either specified in the velocity.properties, if any, or the default, "text/html" if not specified in the properties.
void mergeTemplate( Template, Context, HttpServletResponse ) 允许你创建输出流。VelocityServlet使用了一个很有效的Writer类的缓存池,所以在一般情况下,该方法不会被覆盖。Allows you to produce the output stream. The VelocityServlet uses a pool of very efficient Writer classes, so this would usually be overridden in special situations.
void requestCleanup( HttpServletRequest, HttpServletResponse , Context ) 允许你在请求处理结束后做一些清理工作或者资源的回收工作。默认的方法没有做任何事情。Allows you to do any cleanup or resource reclamation at the end of the request processing. The default does nothing.
protected void error( HttpServletRequest, HttpServletResponse, Exception ) 如果在请求处理中出现异常,就将调用该方法来处理异常。默认的处理将会返回一个普通的带有栈信息和错误信息的HTML页面给用户。覆盖该方法提供更人性化的错误提示或者异常处理。Error handler that is called an exception occurrs in request processing. Default implementation will send a simple HTML message with stacktrace and exception information back to the user. Override for custom client messages and more advanced problem handling.
更多的信息请参见Javadoc API文档。For further information, please see the Javadoc API documentation.
部署Deployment
当你部署基于Velocity的servlet应用,你试图确定你的配置文件被用来配置了Velocity引擎。在Tomcat下,达到该目的的方法之一是将你的velocity.properties文件放在你的web应用的根目录下(webapps/appname),并在你的WEB-INF/web.xml文件后面加上:When you deploy your Velocity-based servlets, you will certainly want to ensure that your properties file is used to configure the Velocity runtime. Under Tomcat, one way to accomplish this is by placing your velocity.properties file into the root directory of your web app (webapps/appname ) and then add the following to your WEB-INF/web.xml file : <servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.foo.bar.MyServlet</servlet-class>
<init-param>
<param-name>properties</param-name>
<param-value>/velocity.properties</param-value>
</init-param>
</servlet>
假设一切正常,在MyServlet被加载的时候,会使用velocity.prooetries文件来初始化Velocity引擎。Assuming all is right, this will ensure that when MyServlet is loaded, it will use the velocity.properties file to initialize itself rather than relying on it's internal defaults.
注意Velocity在它的核心的Runtime类中使用的是单例模式,所以,把velocitypXX.jar放在WEB-INF/lib文件夹中会是一个更好的选择。Note that Velocity uses a singleton model for it's central core Runtime class, so it is a very good idea to put the velocity-XX.jar into the WEB-INF/lib directory in all web applications that use Velocity to ensure that the web app classloader is managing your Runtime instance, rather than putting it in the CLASSPATH or the top level lib directory of the servlet runner.
这种部署方式能确保在不同的web应用中使用的Velocity配置不会冲突。This deployment method will ensure that different web applications will not be subject to Velocity configuration conflicts. 在普通的应用中使用Velocity
Using Velocity In General Applications
Velocity被设计为一个通用的工具,在一般的应用中,Velocity也很有用。概括地说,在一般的应用中,使用Velocity的基本结构和在本指南开始介绍的那个结构一致,和VelocityServlet提供给了编写Servlet的便利性一样,Velocity也为一般的应用提供了一些工具类。作为一个一般应用的程序员,你的一个新的责任就是初始化Velocity运行时引擎(Velocity runtime engine),但这也很简单。As Velocity was designed to be a general-use tool, it is just as useful in general application programs as it is servlets. In general, you can use the same programming pattern discussed at the beginning of this guide, but there are a few utility methods provided for application use, just like we provide the VelocityServlet base class for ease of use in servlet programming. The only new responsibility you have as the application programmer is to initialize the Velocity runtime engine, but that is easy.
The Velocity Helper Class
Velocity包含了一个为一般应用准备的工具类,叫做Velocity(org.apache.velocity.Velocity)。提供该类的目的在于提供一些初始化Velocity必须的方法和一些有用的日常工具方法,让使用Velocity更加方便。这个类在Javadoc中有详细记录,如有需要,请参考该文档。在这里,该文档只是作为一个指南,而Javadoc则提供了最完整的API的信息。Velocity contains an application utility class called Velocity ( org.apache.velocity.app.Velocity ). The purpose of this class is to provide the necessary methods required to initialize Velocity, as well as useful utility routines to make life easier in using Velocity. This class is documented in the project's javadoc, so please look there for definitive details. This documentation is intended to be of a tutorial nature; therefore for compete API information, the Javadoc is the definitive source.
Velocity运行时引擎是一个单例的实例,在一个JVM中为所有的Velocity用户提供资源,日志记录,和其他的服务。不过,运行时引擎只需要初始化一次即可。你可能会尝试多次初始化该引擎,但只有第一次的初始化操作是成功的,之后的尝试会直接被忽略。现在的Velocity工具类提供了5个方法来配置运行时引擎。The Velocity runtime engine is a singleton instance that provides resource, logging and other services to all Velocity users running in the same JVM. Therefore, the runtime engine is initialized only once. You can attempt to initialize Velocity more than once, but only the first initialization will apply. The rest of the attempts will be ignored. The Velocity utility class currently provides five methods used in configuration of the runtime engine.
这5个方法分别为:The five configuration methods are :
setProperty( String key, Object o )
给属性key赋值o。该值一般为一个String,但在特殊的情况下,可以是用逗号分隔了的列表值(在一个String中,比如:"foo,bar,woogie")或者其他的一些东西。Sets the property key with the value o. The value is typically a String, but in special cases can also be a comma-separated list of values (in a single String, ex."foo, bar, woogie") as well as other things that will arise. Object getProperty( String key )
返回属性key的值。注意你必须强制的转化类型,不然,返回的总是String。Returns the value of the property key. Note that you must be aware of the type of the return value, as they can be things other than Strings. init()
使用分发包中的属性初始化运行时状态。(这些值在下面的关于属性小节中列出)Initializes the runtime with the default properties provided in the distribution.(These are listed below in the section pertaining to properties.) init( Properties p )
使用参数中传入的java.util.Properties对象中包含的属性来初始化运行时状态。Initialize the runtime with the properties contained in the java.util.Properties object passed as an argument. init( String filename )
使用参数中传入的文件路径对应的属性文件来初始化运行时状态。initilizes the runtime using the properties found in the properties file filename
注意,不论使用哪种方法,默认的属性都会作为基础的配置,所有增加的或者修改了的属性都会覆盖对应的默认的配置。没有被覆盖的属性仍然起作用。这样的好处是只有你关注的属性才被指定,而不需要把所有的属性都指定出来。Note that in each case, the default properties will be used as a base configuration, and any additional properties specified by the application will replace individual defaults. Any default properties not overwritten will remain in effect. This has the benefit that only the properties you are interested in changing need to be specified, rather than a complete set.
另一个需要注意的是init()方法可以被多次调用但并不会出现错误。但是,只有第一次调用init()方法才将使用配置属性集来配置引擎,而其后对配置属性的更改或者重新调用init()方法都会被忽略。Another thing to note is that the init() calls may be called more than once without harm in an application. However, the first call to any of the init() functions will configure the engine with the configuration properties set at that point, and any further configuration changes or init() calls will be ignored.
最普通的初始化Velocity的方法会像是这样:The most common approaches to initializing Velocity will be something like :
用类似于org/apache/velocity/runtime/defaults/velocity.properties文件的格式那样将自己的需要的配置属性的值写入一个文件,或者放入一个java.util.Properties中,并且调用init(filename)或者init(Properties)方法。Setup the configuration values you wish to set in a file in the same format as org/apache/velocity/runtime/defaults/velocity.properties (the default set), or in a java.util.Properties, and then call either init( filename ) or init( Properties ) 使用setProperty()方法独立的设置配置属性,并且调用init()方法。这个方法一般在比较高级的情况下------应用拥有自己的配置管理系统,比如应用使用它自己生成的配置文件在运行时配置Velocity。Set the configuration values individually using setProperty() and then call init(). This method is generally used by more advanced applications that already have their own configuration management system - this allows the application so configure Velocity based upon values it generates at runtime, for example.
一旦运行时被初始化,你可以做你想做的任何事情了,比如把模版合成并放入一个输出流中,Velocity提供的工具类能帮你很简单的完成这些任务。下面是一些该工具类的主要的方法的描述。Once the runtime is initialized, you can do with it what you wish.. This mostly revolves around rendering templates into an output stream, and the Velocity utility class allows you to do this easily. Currently, here are the methods and a brief description of what they do :
evaluate( Context context, Writer out, String logTag, String instring )
evaluate( Context context, Writer writer, String logTag, InputStream instream )
这些方法将使用你提供的上下文来解释输入流---以String或InputStream对象存在,并放入一个输出Writer中。这是一个很方便的用字符串替换标识的方法,如果你坚持使用VTL'模版'-----即使这些'模版'是保存在数据库中的,或者非文件形式的甚至直接是应用生成的。These methods will render the input, in either the form of String or InputStream to an output Writer, using a Context that you provide. This is a very convenienient method to use for token replacement of strings, or if you keep 'templates' of VTL-containing content in a place like a database or other non-file storage, or simply generate such dynamically. invokeVelocimacro( String vmName, String namespace, String params[], Context context, Writer writer )
允许直接使用Velocity宏。这也能通过上面介绍的evaluate()方法达到。这里,你需要指定你想要执行的宏的名字,创建一个传递给VM的参数的数组,一个带有数据的上下文对象和一个输出Writer。注意传递给VM的参数必须是在上下文中对应的值得key值,而不是该宏真正要使用的参数值。就是说,宏真正得到的参数是通过传入的key来从上下文中得到的值。这一个特性可能会在以后改变。Allows direct access to Velocimacros. This can also be accomplished via the evaluate() method above if you wish. Here you simply name the vm you wish to be called, create an array of args to the VM, a Context of data, and Writer for the output. Note that the VM args must be the 'keys' of the data objects in the Context, rather than literal data to be used as the arg. This will probably change. mergeTemplate( String templateName, Context context, Writer writer )
方便的调用Velocity的模版处理和合成服务的方法。该方法会处理并合成模版。更进一步的,该方法也会通过对文件资源加载器设置的属性来加载模版,并且提供Velocity的文件和模版的缓存器。这是最有效的访问模版的方法,除非你有特殊的需求,我们建议你使用该方法。Convenient access to the normal template handling and rendering services of Velocity. This method will take care of getting and rendering the template. It will take advantage of loading the template according to the properties setting for the file resource loader, and therefore provides the advantage of file and parsed template caching that Velocity offers. This is the most efficient way to access templates, and is recommended unless you have special needs. boolean templateExists( String name )
测试一个名字为name的模版文件是否能在当前配置的资源加载器中找到。Determines if a template name is able to be found by the currently configured resource loaders.
一旦我们了解了这些基本的助手方法,我们就能很容易的使用Velocity写出Java程序了,如下面所示:Once we know about these basic helpers, it is easy to write Java program that uses Velocity. Here it is:
import java.io.StringWriter;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.VelocityContext;public class Example2
{
public static void main( String args[] )
{
/* first, we init the runtime engine. Defaults are fine. */Velocity.init();/* lets make a Context and put data into it */VelocityContext context = new VelocityContext();context.put("name", "Velocity");
context.put("project", "Jakarta");/* lets render a template */StringWriter w = new StringWriter();Velocity.mergeTemplate("testtemplate.vm", context, w );
System.out.println(" template : " + w );/* lets make our own string to render */String s = "We are using $project $name to render this.";
w = new StringWriter();
Velocity.evaluate( context, w, "mystring", s );
System.out.println(" string : " + w );
}
当我们运行这个程序,并且testtemplate.vm也在应用的同一个目录下(因为我们这里使用的是默认的资源加载器,会在当前目录下加载模版文件),我们的输出应该是:When we run this program, and have the template testtemplate.vm in the same directory as our program (because we used the default configuration properties, and the defaul place to load templates from is the current directory...), our output should be : template :
Hi! This Velocity from the Jakarta project.
string : We are using Jakarta Velocity to render this.
testtemplate.vm的内容是:where the template we used, testtemplate.vm, is
Hi! This $name from the $project project.
这就是在普通应用中使用Velocity~注意我们没有必要在一个应用中同时mergeTemplate()和evaluate()方法。在这里同时使用它们是为了演示的需要。你可能只需要其中一个,但也要看你应用的需求来选择。That's all there is to it! Note that we didn't have to use both mergeTemplate() and evaluate() in our program. They are both included there for demonstration purposes. You will probably use only one of the methods, but depending on you application requirements, you are free to do what you wish.
这段代码和我们在指南开始时提到的基础结构有一些区别。第一,你创建了一个上下文对象并使用需要的数据填充,但在这个例子中,mergeTemlate()方法被使用了,mergeTemplate()方法为你处理了模版并且合成了输出------使用的正是对底层的Runtime类的调用。在第二个例子中,你是通过一个String对象作为模版的,所以,代码忽略了寻找模版的步骤,并且evaluate()调用底层方法为你合成了输出。This appears to be a little different from the 'fundamental pattern' that was mentioned at the beginning of this guide, but it really is the same thing. First, you are making a context and filling it with the data needed. Where this examples differs is that in the part of the above example where mergeTemplate() is used, mergeTemplate() is doing the work of getting the template and merging it for you, using the lower-level calls in the Runtime class. In the second example, you are making your template dynamically via the String, so that is analgous to the 'choose template' part of the process, and the evaluate() method does the merging for you using lower level calls.
So the example above sticks to the same simply pattern of using the Velocity template engine, but the utility functions do some of the repeated drudge work, or allow you other options for your template content other than template files.
异常Exceptions
在解析和合成的过程中,会有三种异常可能被抛出。这些是除了IO错误之外的错误,在org.apache.vleocity.exception包中可以找到它们:There are three exceptions that Velocity will throw during the parse / merge cycle. This are additional to the exceptions that will come from IO problems, etc. They are found in the package org.apache.velocity.exception and are:
ResourceNotFoundException
当资源管理系统不能找到一个请求的资源(模版)的时候抛出。Thrown when the resource managment system cannot find a resource (template) that was requested. ParseErrorException
当在解析一个资源(模版)发现语法错误的时候抛出。Thrown when a VTL syntax error is found when parsing a resource (template). MethodInvocationException
当在合成时期,上下文中一个对象的方法执行期间抛出异常时抛出。该出错方法抛出的错误将会被包装并传递给应用。这要求你在运行时处理自己的对象抛出的错误。Thrown when a method of object in the context thrown an exception during render time. This exception wraps the thrown exception and propogates it to the application. This allows you to handle problems in your own objects at runtime.
一旦以上的情况发生,一个错误信息就会被放入运行时日志中。更多信息请参见Javadoc API文档。In each case, a message is put into the runtime log. For more information, see the Javadoc API documentation.
其他细节Miscellaneous Details
尽管上面的例子使用的是默认的配置,但是要使用自定义的配置也是很容易的事情。你需要做的就是创建一个属性文件,并将该文件的路径名通过init(String)方法或者通过创建一个自定义属性名/值的java.util.Properties对象并调用init(Properties)方法既可以了。后者更为方便,因为你既可以通过使用load()方法从某个属性文件中加载属性,也可以在运行时动态的从你的应用或者框架的配置文件中得到。这给了你将关于应用的所有属性都配置在一个文件中的灵活性。While the above example used the default properties, setting your own properties is very simple. All you have to do is make a properties file somewhere and pass the name of that file to the init(String) method of the Velocity utility class, or make a java.util.Properties object, add the desired properties and values, and pass that to the init(Properties) method. The latter method is convenient, because you can either fill it directly from a separate properties file via the load() method, or even better, you can fill it dynamically from your own application / framework's property set at runtime. This gives you the freedom to combine all of the properties for your app into one properties file.
如果我们想使用一个不同的文件夹来存放模版文件,我们就可以象这样来加载它们:If we wanted to use a different directory than the current directory to load our template from, we could do something like this :
...import java.util.Properties;
...public static void main( String args[] )
{
/* first, we init the runtime engine. */Properties p = new Properties();
p.setProperty("file.resource.loader.path", "/opt/templates");
Velocity.init( p );/* lets make a Context and put data into it */...
现在,假设你有一个/opt/templates文件夹,并且有一个叫做testtemplate.vm的模版文件在里面,那么一切都将正常工作。如果你这样做后发生了一些错误,那么请确认velocity.log中的记录来得到更多的信息------错误消息能很快地确定哪里出错了。And, assuming you have a directory /opt/templates and the template testtemplate.vm is in there, then things would work just fine. If you try this and have a problem, be sure to look at the velocity.log for information - the error messages are pretty good for figuring out what is wrong. 应用属性
Application Attributes
应用属性是名/值对,在RuntimeInstance中被访问。Application Attributes are name-value pairs that can be associated with a RuntimeInstance (either via the VelocityEngine or the Velocity singleton) and accessed from any part of the Velocity engine that has access to the RuntimeInstance.
Contents
概述和入门 资源 Velocity怎样工作
基本的格式
使用Singleton还是不使用
Singleton模式 Separate Instance
上下文
基础原则 对使用#foreach()遍历对象的支持 上下文链 模版创建的对象 其他问题
在Servlets里使用
Servlet编程 部署Deployment
在一般的应用中使用
Velocity Helper对象 异常 更多的细节
应用的属性 EventCartridge 和 Velocity的配置 配置日志系统
使用现有的Log4jUsing 一个自定义日志的例子
配置资源加载器(模版加载器)
资源加载器 配置示例 插拔式的资源管理器和资源缓存
模版的编码和国际化 Velocity 和 XML FAQ (Frequently Asked Questions) 总结Summary 附录1:部署示例ServletAppendix
Jakarta Tomcat Caucho Technology's Resin BEA
概述
Velocity是一个基于Java技术的模版引擎,是一个简单但强大的使你能轻松的创建和合成具有一定格式,用于展现数据的文档的工具。在本指南中,我们希望能使你对使用Velocity能有一个比较全面的认识。主要关注两个主要的使用Velocity的领域:
基于servlet的web应用。servlet-based WWW development 普通的应用。general application use
你将会发现其实两者之间的差别很小。当使用我们提供的VelocityServlet作为基于servlet的web应用的基础,加上我们提供的一些工具类,构建一个web应用会及其的简单。
入门Getting Started
即使在Velocity官方网站还是在Velocity的文档中,都能找到下面的信息,但是为了完整这里还是再提下。要使Velocity在你的电脑上开始工作是很简单的事情。注意下面提到的文件夹都是从你的Velocity发布文件的根目录开始的。
得到Velocity的发布文件。 如果你还没有安装Jakarta Ant这个构建工具,请安装好它。需要Ant是为了构建Velocity,而不是为了使用Velocity。 切换到发布文件的build文件夹下。 输入 ant <build target>,其中,<build target>将会是下面之一:
jar 在bin文件夹下构建完整的Velocity jar文件。这个jar文件会命名为Velocity-X.jar,其中X是当前Velocity的版本号。注意这个jar文件没有包括Velocity必需的库文件。如果你使用了这个任务,你需要从JAakarta Commons上得到Collections组件,并且将该jar文件添加到你的CLASSPATH中(或者放到WEB-INF/lib)。如果你希望使用日志或其他功能,你也需要把相关的jar文件添加到CLASSPATH或者WEB-INF/lib中。为了方便,你可以直接使用jar-dep任务来将需要的jar都打包到一起。 jar-dep 在bin文件夹下构建完整的Velocity jar文件,包括了Velocity所有依赖的库文件。 jar-core 在bin文件夹下构建一个小型的Velocity jar文件,该文件叫做velocity-core-X.jar。该jar中只包括了Velocity最核心的功能,不包括例子和工具,比如Anakia,Texen,VelocityServlet等。关于该jar文件的依赖组件和jar任务一样,也没有包括。 jar-util 在bin文件夹中构建Velocity的工具jar,该文件叫做velocity-util-X.jar。该jar中只包含了Velocity提供的工具,Anakia,Texen和WebMacro模版转换工具。关于该jar文件的依赖组件和jar任务一样,也没有包括。 jar-servlet 在bin文件夹中构建Velocity的工具jia,该文件叫做velocity-servlet-X.jar。该jar包含了servlet相关的工具代码。关于该jar文件的依赖组件和jar任务一样,也没有包括。 jar-J2EE 构建一个完整的jar文件。和jar任务相似,该jar文件包含了J2EE应用所需要的所有的组建。现在,多增加的文件就只有org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader。和其他任务一样,该jar文件也是放在bin文件夹下的,叫做velocity-j2ee-X.jar。注意,如果你希望使用该任务,你必须将j2ee.jar拷贝到build/lib文件夹中。我们没有在任何发布文件中提供该jar文件。在http://java.sun.com上能找到他。关于该jar文件的依赖组件和jar任务一样,也没有包括。 jar-J2EE-dep build a complete jar with J2EE support and includes logging support from the Jakarta Avalon Logkit and regexp support fromt the Jakarta ORO package. See the notes on the jar-dep target, above. examples 构建在examples文件夹中的例子代码。该任务同样会构建论坛例子项目。 forumdemo 构建在examples/forumdemo文件夹中的web应用实例。 docs 在docs文件夹中使用Anakia XML转换工具构建文档。允许你在构建中使用Velocity模版作为样式表。注意,该任务需要把jakarta-site2项目放在jakarta-velocity发布文件夹中。关于更详细的内容请参看在build.xml文件中该任务的详细说明。 jar-src 将所有的Velocity代码放在一个jar里面,置于bin文件夹中。 javadocs 在doc/api文件夹中构建API文档。 test (after jar) will test Velocity against it's testbed suite of test routines help 列出所有可用的任务。
虽然不是必须的,但首先测试一下构建是一个不错的想法。使用test任务即可。 所有的事情都OK了。Velocity已经能开始工作了。将合适的jar文件置于你的类路径中,或者放在合适的位置(比如web-inf/lib) 我们建议你先运行下例子来感受下Velocity。通过输入ant examples来构建。
依赖包Dependencies
Velocity使用Java2 API。构建Velocity需要Java2 SDK。要运行Velocity,需要Java2 RTE。
Velocity为了完成一些通用的功能,需要一些额外的包。为了方便,他们都放在build/lib文件夹下,但是默认的构建任务并没有包含这些jar。如果你使用的是默认的任务构建Velocity,你必须要把这些jar文件放入你的classpath。
Jakarta Commons Collections - 必需的required. Jakarta Avalon Logkit - 可选的,但是很常用。如果需要使用Velocity默认的基于文件的日志功能。 Jakarta ORO - 可选的。如果要使用org.apache.velocity.convert.WebMacro 模版转换工具。optional. Needed when using the org.apache.velocity.convert.WebMacro template conversion utility. 资源Resources 下面有一些例子和资源。我们建议你能看看我们提供的例子,文档甚至源代码。下面是一些很好的资源。 用户和开发者的交流社区:通过mail-lists加入我们。 Mail-list资料库: http://www.mail-archive.com 。在搜索框中输入velocity来查看user和dev的文档。 源代码:在Velocity项目下的src/java文件夹中。 应用示例1:一个简单的用于演示怎样在一个应用程序中使用Velocity。 应用示例2:一个简单的用于演示怎样通过使用Velocity应用程序工具包来在应用程序中使用Velocity。 servlet例子:一个简单的用于演示怎样在servlet中使用Velocity的例子。 日志例子:一个简单的用于演示怎样自定义一个日志记录器并将它注册到velocity中来接收日志消息的例子。 XML例子:examples/xmlapp_example : a simple example showing how to use JDOM to read and access XML document data from within a Velocity template. It also includes a demonstration of a recursive Velocimacro that walks the document tree. event 例子:一个用来演示在Velocity1.1中出现的事件处理API的使用。 Anakia 应用:application : 一个用来演示怎样使用Velocity来为XML数据创建样式表。 论坛Web演示应用Forumdemo web app :一个基于servlet的论坛演示应用。 documentation : docs : all the generated documentation for the Velocity project in html API documentation : docs/api : the generated Javadoc documentation for the Velocity project 模版:大量的关于模版的示例,这些都是极好的VTL的例子。 上下文例子:两个关于怎样扩展上下文对象的例子,适用于高级用户。
上面所有引用到的文件夹都是基于发布文件的根目录的。 Velocity怎样工作
How Velocity Works 基本的格式'The Fundamental Pattern'
当你在一个应用程序中或者一个servlet中,你一般会按照下面的方式来做。When using Velocity in an application program or in a servlet (or anywhere, actually), you will generally do the following :
初始化Velocity:对于两种使用环境都适用------Singleton和separate rutime instance,并且该工作只需做一次。Initialize Velocity. This applies to both usage patterns for Velocity, the Singleton as well as the 'separate runtime instance' (see more on this below), and you only do this once. 创建一个上下文对象(后面将详细介绍)。Create a Context object (more on what that is later). 把你的数据对象放入上下文对象中。Add your data objects to the Context. 选择一个模版。Choose a template. 把模版和你的数据一起输出。'Merge' the template and your data to produce the ouput.
在代码中,通过类org.apache.velocity.app.Velocity使用Singleton模式,代码如下:In code, using the singleton pattern via the org.apache.velocity.app.Velocity class, this looks like import java.io.StringWriter;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.Template;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.MethodInvocationException;
Velocity.init();
VelocityContext context = new VelocityContext();
context.put( "name", new String("Velocity") );
Template template = null;
try
{
template = Velocity.getTemplate("mytemplate.vm");
}
catch( ResourceNotFoundException rnfe )
{
// couldn't find the template
}
catch( ParseErrorException pee )
{
// syntax error : problem parsing the template
}
catch( MethodInvocationException mie )
{
// something invoked in the template
// threw an exception
}
catch( Exception e )
{
}
StringWriter sw = new StringWriter();
template.merge( context, sw );
这就是基本的格式。很简单,不是吗?这基本上就是你使用Velocity来合成模版所需要的步骤。当然在实际的应用中你不会像这样书写代码,我们为servlet和普通应用提供了一些工具,让你能甚至比上面的代码更容易的使用Velocity。在本指南的后面,我们将介绍在servlet和普通应用中使用Velocity的更多细节,和那些不错的工具。在每一种情况下,当然,上面的运行的顺序是不变的,或者在幕后是这样的。That's the basic pattern. It is very simple, isn't it? This is generally what happens when you use Velocity to render a template. You probably won't be writing code exactly like this - we provide a few tools to help make it even easier than this for both servlet and application programmers. Later on in this guide, we will talk about using Velocity in both servlets as well as general applications, and we discuss the tools we provide to make things easier. In each case, though, the above sequence is what is happening either explicitly, or behind the scenes. 使用singleton或者不...To Singleton Or Not To Singleton... 在Velocity1.2之后,开发人员就有了两种使用Velocity引擎的选择------单例模式或者非单例模式。两中选择都使用的是相同的Velocity代码,让Velocity更简单的融入到你的Java应用中。As of Velocity 1.2 and later, developers now have two options for using the Velocity engine, the singleton model and the separate instance model. The same core Velocity code is used for both approaches, which are provided to make Velocity easier to integrate into your Java application.
单例模式Singleton Model
这是一种老的模式,即在JVM中只存在一个Velocity引擎的实例,整个应用共享这一个。这种方式对于配置文件的定位和共享资源都是很方便的。比如,该模式极其适合使用在Servlet2.2+标准下的web应用中------每个web应用都拥有其唯一的一个Velocity实例,允许web应用的servlet共享诸如模版,日志记录器等资源。这个单例的实例可以通过org.apache.velocity.app.Velocity.app.Velocity类创建。下面是一个使用单例模式的例子:This is the legacy pattern, where there is only one instance of the Velocity engine in the JVM (or web application, depending) that is shared by all. This is very convenient as it allows localized configuration and sharing of resources. For example, this is a very appropriate model for use in a Servlet 2.2+ compliant web application as each web application can have it's own instance of Velocity, allowing that web application's servlet to share resources like templates, a logger, etc. The singleton is accessable via the org.apache.velocity.app.Velocity class, and and example of use : import org.apache.velocity.app.Velocity;
import org.apache.velocity.Template;
...
/*Configure the engine - as an example, we are usingourselves as the logger - see logging examples*/
Velocity.setProperty( Velocity.RUNTIME_LOG_LOGSYSTEM, this);
/*now initialize the engine*/
Velocity.init();
...
Template t = Velocity.getTemplate("foo.vm");
请注意,在org.apache.velocity.servlet.VelocityServlet 基础类(一个用于使创建servlet更简单的工具类)中,使用的就是单例模式。通过继承该类创建使用Velocity的servlet是最简单和最方便的方式,当然,这不是必须的。serbletPlease note that the Singleton model is used in the org.apache.velocity.servlet.VelocityServlet base class, a utility class provided with the distribution to make writing servlets easier. While extending this class is the most common and convenient way to write servlets using Velocity, you are free to not use this class if you needs require something different.
分离的实例Separate Instance
在1.2中的新出现的分离的实例模式允许你在一个JVM中创建,配置和使用许多Velocity引擎的实例。如果你想在同一个应用中为不同的Velocity引擎配置不同的资源,模版,日志记录器等,这种方式特别有用。通过使用org.apache.velocity.app.VelocityEngine来创建分离实例模式。下面是一个例子,功能同上一个例子:New in version 1.2, the separate instance allows you to create, configure and use as many instances of Velocity as you wish in the same JVM (or web application.) This is useful when you wish to support separate configurations, such as template directories, loggers, etc in the same application. To use separate instances, use the org.apache.velocity.app.VelocityEngine class. An example, which parallels the above singleton example, looks like :
import org.apache.velocity.app.[VelocityEngine];
import org.apache.velocity.Template;
...
/*create a new instance of the engine*/
VelocityEngine ve = new VelocityEngine();
/*configure the engine. In this case, we are usingourselves as a logger (see logging examples..)*/
ve.setProperty( VelocityEngine.RUNTIME_LOG_LOGSYSTEM, this);
/*initialize the engine*/
ve.init();
...
Template t = ve.getTemplate("foo.vm");
如你所见,这是很简单并且很直接的。除了一些简单的形式上的改变,使用单例模式还是分离模式对于你应用的高层次的架构和模版是没有影响的。As you can see, this is very simple and straightforward. Except for some simple syntax changes, using Velocity as a singleton or as separate instances requires no changes to the high-level structure of your application or templates.
作为一个程序员,需要强调的是使用org.apache.velocity.app.Velocity类来应用单例模式,使用org.apache.velocity.app.VelocityEngine类来应用非单例模式(分离实例模式)。As a programmer, the classes you should use to interact with the Velocity internals are the org.apache.velocity.app.Velocity class if using the singleton model, or org.apache.velocity.app.VelocityEngine if using the non-singleton model ('separate instance').
另外,不要使用在org.apache.velocity.runtime包中的Runtime,RuntimeConstants,RuntimeSingleton或者RuntimeInstance类。这些类是用于框架内部的,并且在不断的变化中。正如上面提到的,你应该使用在org.apache.vleocity.app包中的类------Velocity和VelocityEngine。At no time should an application use the internal Runtime, RuntimeConstants, RuntimeSingleton or RuntimeInstance classes in the org.apache.velocity.runtime package, as these are intended for internal use only and may change over time. As mentioned above, the classes you should use are located in the org.apache.velocity.app package, and are the Velocity and VelocityEngine classes. If anything is missing or needed from those classes, do not hesitate to suggest changes - these classes are intended for the application developer. 上下文对象The Context 基础The Basics
Velocity是以上下文概念为中心的,并且这是一种普通的在系统的各个部分中传递数据容器的技术。可以这样认为,上下文是在Java层(程序员关注的)和模版层(设计人员关注的)之间数据的负载者。程序员会将所有应用需要的对象,无论什么类型都聚集在一起,放入上下文中。对于页面设计者,在上下文中的对象,和它们的属性及方法都通过叫做引用的模版元素来调用。一般来说,你需要和设计者一起设计在页面上需要显示的数据,这些会成为设计者在页面中需要使用的API。所以,在这个阶段的时间的投入和仔细的分析是很值得的。The concept of the 'context' is central to Velocity, and is a common technique for moving a container of data around between parts of a system. The idea is that the context is a 'carrier' of data between the Java layer (or you the programmer) and the template layer ( or the designer ). You as the programmer will gather objects of various types, whatever your application calls for, and place them in the context. To the designer, these objects, and their methods and properties, will become accessable via template elements called references. Generally, you will work with the designer to determine the data needs for the application. In a sense, this will become an 'API' as you produce a data set for the designer to access in the template. Therefore, in this phase of the development process it is worth devoting some time and careful analysis.
同时,Velocity允许你创建自己的上下文对象来支持特别的要求和技术(比如连接一个LDAP的上下文)。一个很好的扩展点是继承VelocityContext类。While Velocity allows you to create your own context classes to support special needs and techniques (like a context that accesses an LDAP server directly, for example), a good basic implementation class called VelocityContext is provided for you as part of the distribution.
VelocityContext满足一般的需要,并且我们强烈建议你使用它。只有在例外或者高级的情况下,你需要继承或者创建你自己的上下文对象。VelocityContext is suitable for all general purpose needs, and we strongly recommended that you use it. Only in exceptional and advanced cases will you need to extend or create your own context implementation.
使用VelocityContext和使用普通的Java Hashtable对象差不多。你需要的最主要的两个方法是:Using VelocityContext is as simple as using a normal Java Hashtable class. While the interface contains other useful methods, the two main methods you will use are public Object put(String key, Object value);
public Object get(String key);
请注意这和Hashtable相似,里面的值必须继承自java.lang.Object,并且不是null。基础类型,比如int或者float必须要用适当的包装类包装才能使用。Please note that like a Hashtable, the value must be derived from java.lang.Object, and must not be null. Fundamental types like int or float must be wrapped in the appropriate wrapper classes.
这就是context基础的操作。需要了解更多的信息 ,请参考发布文件中的API文档。That's really all there is to basic context operations. For more information, see the API documentation included in the distribution.
使用*#foreach()来遍历对象{*}Support for Iterative Objects for #foreach()
作为一个程序员,对于放入上下文中的对象你有很大的自由度。在最大范围的自由度里需要遵守一些规则,所以,理解Velocity支持什么,就不会有错误会发生了。Velocity支持几种适合在VTL使用#foreach()指示符的集合类型对象。As a programmer, you have great freedom in the objects that you put into the context. But as with most freedoms, this one comes with a little bit of responsibility, so understand what Velocity supports, and any issues that may arise. Velocity supports serveral types of collection types suitable for use in the VTL #foreach() directive.
Object []正规的对象数组,在这里不需要再多说了。Velocity在框架内部用一个实现了Iterator接口的类来包装你的数组,具体的包装细节作为一个程序员或者模版设计者不需要过多的关注。Regular object array, not much needs to be said here. Velocity will internally wrap your array in a class that provides an Iterator interface, but that shouldn't concern you as the programmer, or the template author. java.util.Collection Velocity会使用iterator()方法来得到一个Iterator对象来执行遍历操作。所以,你需要使你的对象实现Collection接口,以使iterator()方法返回一个正常的Iterator对象。Velocity will use the iterator() method to get an Iterator to use in the loop, so if you are implementing a Collection interface on your object, please ensure that iterator() returns a working Iterator. java.util.Map这里,Velocity依靠values()方法来得到一个Collection接口,然后再调用iterator()方法来得到用来遍历的Iterator对象。Here, Velocity depends upon the values() method of the interface to get a Collection interface, on which iterator() is called to retrieve an Iterator for the loop. java.util.Iterator 小心使用:这是最近才支持的,不过最好是作为临时的使用,主要是因为Iterator的不可复位性决定的。如果一个Iterator直接被放入上下文,并且使用了多于一个的#foreach来遍历,那么第二个#foreach就会失败,因为Iterator并没有复位。USE WITH CAUTION : This is currently supported only provisionally - the issue of concern is the 'non-resettablity' of the Iterator. If a 'naked' Iterator is placed into the context, and used in more than one #foreach(), subsequent #foreach() blocks after the first will fail, as the Iterator doesn't reset. java.util.Enumeration 小心使用:和Iterator相似,这也是最近才支持的用于临时性的。原因也是Enumeration的不可复位性决定的。同样,如果一个Enumeration直接被放入上下文,并且使用了多于一个的#foreach来遍历,那么第二个#foreach就会失败,因为Enumeration并没有复位。USE WITH CAUTION : Like java.util.Iterator, this is currently supported only provisionally - the issue of concern is the 'non-resettablity' of the Enumeration. If a 'naked' Enumeration is placed into the context, and used in more than one #foreach(), subsequent #foreach() blocks after the first will fail, as the Enumeration doesn't reset.
我们建议,只有在必须的时候,才将Iterator 和Enumeration放入上下文中。In the case of the Iterator and Enumeration, it is recommended that they are placed in the context only when it cannot be avoided, and you should let Velocity find the appropriate reusable iterative interface when that is sufficient and possible.
尽管可能有充足理由直接在Context中使用Iterator(比如通过JDBC得到的大的数据集),但如果能够避免,最好还是使用其他的代替。这里直接使用是指象下面代码这样:There are good reasons to use the java.util.Iterator interface directly (large data sets via JDBC, for example), but if it can be avoided, it might be better to use something else. By 'directly' , we meant doing something like: Vector v = new Vector();
v.addElement("Hello");
v.addElement("There");
context.put("words", v.iterator() );
在上面的例子中,Iterator对象自己是放入了Context。作为代替,只需象这样:where the Iterator itself is placed into the context. Instead, if you simply did: context.put("words", v );
这样一切都能正常运行:Velocity将会发现Vector实现了Collection,并且会找到iterator()方法,并且在每一次调用#foreach之前刷新Iterator对象。如果使用的直接是Iterator对象,那么一旦velocity在其上使用了一次#foreach,Velocity没有办法再重新得到一个新的Iterator。这将导致所有在#foreach中使用该引用输出为空。then all would be fine: Velocity would figure out that Vector implement Collection (via List), and therefore will find the iterator() method, and use that to get a 'fresh' Iterator for its use each time it needs to. With just a plain Iterator (the first snippet above...), once velocity has used it in a #foreach(), Velocity has no way of getting a new one to use for the next #foreach() it is used in. The result is no output from any subsequent #foreach() blocks using that reference.
上面的介绍并不是说在Velocity中遍历对象是一件必须经过细致考虑的问题。不过,当你放入上下文中的是一个Iterator,确实需要小心谨慎。This above isn't meant to give the impression that iterating over collections in Velocity is something that requires great care and thought. Rather, the opposite is true, in general. Just be careful when you place an Iterator into the context.
上下文链Context Chaining
Velocity的上下文设计中的一个革命性的创新是加入了上下文链的概念,有时候也称为上下文包装。这种思想让你能将分散的上下文对象连接起来,并看作一个联合的上下文对象来使用。An innovative feature of Velocity's context design is the concept of context chaining. Also sometimes referred to as context wrapping, this advanced feature allows you to connect separate contexts together in a manner that makes it appear as one 'contiguous' context to the template.
最好使用一个例子来演示一下:This is best illustrated by an example : VelocityContext context1 = new VelocityContext();
context1.put("name","Velocity");
context1.put("project", "Jakarta");
context1.put("duplicate", "I am in context1");VelocityContext context2 = new VelocityContext( context1 );context2.put("lang", "Java" );
context2.put("duplicate", "I am in context2");template.merge( context2, writer );
在上面的例子中,我们建立连接context1的context2。这意味着在末拌种,你可以使用任何放在这两个上下文中的对象(但这时不能存在使用同样的key来放入对象)。如果这样做了(使用了重复的key来保存对象),在比较后面定义(外层)的上下文中的那个对象将有效。比如在上面的例子中,如果使用duplicate来引用,得到的将会是"I am in context2"In the code above, we have set up context2 such that it chains context1. This means that in the template, you can access any of the items that were put into either of the two VelocityContext objects, as long as there is no duplication of the keys used to add objects. If that is the case, as it is above for the key 'duplicate', the object stored in the nearest context in the chain will be available. In this example above, the object returned would be the string "I am in context2".
注意这种重复定义(或者叫做覆盖定义),对被覆盖了的对象没有任何害处或者改变。所以,在上面的例子中,字符串"I am in context1"仍然存在并状态良好,并且如果通过使用context1.get("duplicate")就能正确地得到。但在上面的例子中,在模版中使用$duplicate的引用会得到"I am in context2"的值,并且模版上的引用不可能得到"I am in context1"。Note that this duplication, or 'covering', of a context item does not in any way harm or alter the covered object. So in the example above, the string "I am in context1" is alive and well, still accessable via context1.get("duplicate"). But in the example above, the value of the reference '$duplicate' in the template would be 'I am in context2', and the template has no access to the covered string 'I am in context1'.
注意,如果你依赖从模版中向上下文添加一些在合成时需要检查的状态值,你需要小心。通过在模版中使用#set来改变上下文中的值得时候,只会影响最外层的上下文。所以,确保如果你不希望从模版中改变内层上下文的值得时候,不要丢弃外层的上下文。Note also that you have to be careful when you are relying on the template to add information to a context that you will examine later after the rendering. The changes to the context via #set() statements in a template will affect only the outer context. So make sure that you don't discard the outer context, expecting the data from the template to have been placed onto the inner one.
This feature has many uses, the most common so far is providing layered data access and toolsets.
如同前面所介绍的,Velocity的上下文机制也是能被扩展的,不过这超越了本指南的范围。如果你对这感兴趣,请参看在org.apache.velocity.context包中的类是怎样提供上下文对象并把它们联合起来的。同时,在examples/context_example目录下,也有一些例子演示了怎样扩展上下文,包括了使用数据库来作为上下文对象的例子(虽然这是个很愚蠢的想法)。As mentioned before, the Velocity context mechanism is also extendable, but beyond the current scope of this guide. If you are interested, please see the classes in the package org.apache.velocity.context to see how the provided contexts are put together. Futher, there are a few examples in the examples/context_example directory in the distribution which show alternate implementations, including [a goofy] one that uses a database as the backing storage.
请注意这些例子仅仅用于演示,而没有被验证实用价值。Please note that these examples are unsupported and are there for demonstration/educational purposes only.
在模版中创建的对象Objects Created in the Template
有两种情况下,Java代码需要处理在运行时从模版中创建的对象。There are two common situations where the Java code must deal with objects created at runtime in the template :
当一个模版的作者调用了一个放在上下文中的用Java编写的对象的方法的时候:When a template author calls a method of an object placed into the context by Java code. #set($myarr = "a","b","c" )
$foo.bar( $myarr )
当一个模版向上下文中添加了一些在模版合成之后,Java代码需要处理的对象时。When a template adds objects to the context, the Java code can access those objects after the merge process is complete. #set($myarr = "a","b","c" )
#set( $foo = 1 )
#set( $bar = "bar")
处理这些情况只需要了解一些事实:Dealing with these cases if very straighforward, as there are just a few things to know:
在VTL中当把一个范围操作[1..10]或者和对象数组["a","b"]放入上下文中是java.util.ArrayList对象。因此,当你设计的方法是需要接受一个数组的时候,请注意这一点。The VTL RangeOperator [ 1..10 ] and ObjectArray ["a","b"] are java.util.ArrayList objects when placed in the context or passed to methods. Therefore, your methods that are designed to accept arrays created in the template should be written with this in mind. 数字将作为Integer放入上下文中,字符串是String。Numbers will be Integers in the context, and strings will be, of course, Strings. 在方法调用的时候,Velocity也会接受原始值。比如通过#set放入上下文中的一个Integer也能调用setFoo(int i)方法。Velocity will properly 'narrow' args to method calls, so calling setFoo( int i ) with an int placed into the context via #set() will work fine.
关于上下文的其他问题Other Context Issues
One of the features provided by the VelocityContext (or any Context derived from AbstractContext) is node specific introspection caching. Generally, you as a the developer don't need to worry about this when using the VelocityContext as your context. However, there is currently one known usage pattern where you must be aware of this feature.
The VelocityContext will accumulate intropection information about the syntax nodes in a template as it visits those nodes. So, in the following situation:
You are iterating over the same template using the same VelocityContext object. Template caching is off. You request the Template from getTemplate() on each iteration.
It is possible that your VelocityContext will appear to 'leak' memory (it is really just gathering more introspection information.) What happens is that it accumulates template node introspection information for each template it visits, and as template caching is off, it appears to the VelocityContext that it is visiting a new template each time. Hence it gathers more introspection information and grows. It is highly recommended that you do one or more of the following :
Create a new VelocityContext for each excursion down through the template render process. This will prevent the accumulation of introspection cache data. For the case where you want to reuse the VelocityContext because it's populated with data or objects, you can simply wrap the populated VelocityContext in another, and the 'outer' one will accumulate the introspection information, which you will just discard. Ex. VelocityContext useThis = new VelocityContext( populatedVC ); This works because the outer context will store the introspection cache data, and get any requested data from the inner context (as it is empty.) Be careful though - if your template places data into the context and it's expected that it will be used in the subsequent iterations, you will need to do one of the other fixes, as any template #set() statements will be stored in the outermost context. See the discussion in Context chaining for more information. Turn on template caching. This will prevent the template from being re-parsed on each iteration, resulting the the VelocityContext being able to not only avoid adding to the introspection cache information, but be able to use it resulting in a performance improvement. Reuse the Template object for the duration of the loop iterations. Then you won't be forcing Velocity, if the cache is turned off, to reread and reparse the same template over and over, so the VelocityContext won't gather new introspection information each time. 在Servlets中使用Velocity
Using Velocity In Servlets
Servlet编程Servlet Programming
Velocity最常用的地方就是在Web应用中的Java Servlet编程了。有许多理由来解释为什么Velocity很适合这样工作,其中最主要的一个就是Velocity的强制性的将视图层和逻辑层分开了。有许多关于这方面的资料,包括这个。The most common use of Velocity is in the area of Java Servlet programming for the WWW. There are many reasons why Velocity is well suited for this task, one of the primary ones is Velocity's enforcement of the separation of the presentation (or view) layer from the code layer. There are many resources on this subject, including this.
把Velocity应用于servlet环境的基础技术是很简单的,所有你需要做的就是继承VelocityServlet基础类,并实现一个handleRequest()方法,这就是全部。The basic technique of using Velocity in a servlet environment is very simple. In a nutshell, all you must do is extend the provided VelocityServlet base class and implement a single method, handleRequest(). That's really all that is required to use Velocity in your servlet development.
对于Velocity1.1,有两个方法需要实现:As of Velocity 1.1, there are two handleRequest() methods :
public Template handleRequest( Context ) 这是比较老的一个方法,这个方法要求你返回一个合法的Template对象。如果该对象无效或者为null,则被认为是一个错误的状态,并使用error()错误处理方法来处理。如果需要,你可以覆盖error()方法。如果该方法返回null也是你希望的一个正确的返回值(比如你想重定向请求),建议你使用下面一个比较新的方法。This is the older of the two methods. This method requires that you return a valid Template object. If not valid, or null, this is considered an error condition, and will result in the error() error handling method being called. You may override the error() if you wish. If returning a null is something you expect to do (for example, you will want to redirect requests) it is recommended that you use the newer method, listed next.
public Template handleRequest( HttpServletRequest, HttpServletResponse, Context ) 这个方法比较新,在1.1中实现。这两者最大的区别在于在这个方法中,HttpServletReuqest和HttpServletResponse对象是作为参数传递给方法的。灵鸽一个不同的地方在于这个方法允许返回一个null来表明在该方法中所有的处理已经做完了,Velocity只能再调用requestCleanup()方法。这个方法在你需要重定向请求的时候特别有用。This is the newer of the two handleRequest() methods, implemented in version 1.1. The difference with this method is that the HttpServletRequest and HttpServletResponse objects are passed to you as arguments to the method, as well as in the Context. The other difference is that this method can return null to indicate that all processing has been handled by the method, and that Velocity should do nothing further than call requestCleanup(). This is extremely useful is you wish to redirect the request, for example. 同样,请参看Javadoc API文档来得到更多的信息。As always, please refer to the Javadoc API documentation for the definitive and latest notes.
下面这个例子和在发布文件中的SampleServlet.java文件(在example文件夹下)相似。The following code is similar to the SampleServlet.java class included in the distribution in the examples directory.
public class SampleServlet extends VelocityServlet
{
public Template handleRequest( HttpServletRequest request,
HttpServletResponse response,
Context context )
{String p1 = "Jakarta";
String p2 = "Velocity";Vector vec = new Vector();
vec.addElement( p1 );
vec.addElement( p2 );context.put("list", vec );Template template = null;try
{
template = getTemplate("sample.vm");
}
catch( ResourceNotFoundException rnfe )
{
// couldn't find the template
}
catch( ParseErrorException pee )
{
// syntax error : problem parsing the template
}
catch( Exception e )
{}return template;
}
}
很眼熟?创建上下文对象的错误处理在VelocityServlet中已经处理,并且merge()方法的调用也在Velocity基础类中处理了。总的来说,这和我们在介绍Velocity的使用的基本格式的时候是一致的。在该例子中,我们向context中放入了一些应用数据,然后返回了一个模版。Look familiar? With the exception of creating the context object, which is done for you by the VelocityServlet base class, and the merge() step which is also done for you by the VelocityServlet base class, it's identical to the basic code pattern we mentioned at the beginning of this guide. We take the context, add our application data, and return a template.
在传入方法(第一个方法)的Context对象中,保存了当前的HttpServletRequest和HttpServletResponse对象。他们使用VelocityServlet.REQUEST(value="req")和Velocity.RESPONSE(VALUE="RES")常量来引用。要在你的代码中使用这些对象,象下面这样做:The default Context object that is passed into the handleRequest() methods contains both the current HttpServletRequest and HttpServletResponse objects. They are placed in the context using the the constants VelocityServlet.REQUEST (value = 'req') and VelocityServlet.RESPONSE (value = 'res') respectively. To access and use these objects in your Java code : public Template handleRequest( Context context )
{
HttpServletRequest request = (HttpServletRequest) context.get( REQUEST );
HttpServletResponse response = (HttpServletResponse) context.get( RESPONSE );
...
或者在你的模版中这样引用:and in your templates: #set($name = $req.getParameter('name') )
如果需要更高级的使用,VelocityServlet基础类允许你覆盖请求处理的一些部分,下面的这些方法可能是一个扩展点:For more advanced uses, the VelocityServlet base class allows you to override parts of the handling of the request processing. The following methods may be overridden :
Properties loadConfiguration( ServletConfig ) 允许你覆盖普通的配置机制并且添加或者修改一些配置属性。覆盖这个方法用来覆盖或者参数化模版和日志的路径,该路径需要基于webapp root的绝对路径。Allows you to override the normal configuration mechanism and add or alter the configuation properties. This is useful for overriding or augmenting template and log paths, to set the absolute path into the webapp root at runtime.
Context createContext(HttpServletRequest, HttpServletResponse ) 与许你创建自己的Context对象。这在需要更高级的情况下使用,比如需要链化或者预加载一些工具或者数据。默认的该方法的实现简单的返回了一个包含了HttpServletRequest和HttpServletResponse的VelocityContext对象。请求和响应对象使用简单的包装类包装,以避免在一些servlet容器中自省时会发生的错误。但你可以象平常一样使用其中的request和response对象,或者从模版中引用其方法。只需要注意一点,就是他们不再是javax.servlet.XXXX类了,如果你需要使用类全名的时候,需要重视。Allows you to create the Context object yourself. This allows more advanced techniques, such as chaining or pre-loading with tools or data. The default implementation simply returns a VelocityContext object with the request and response objects placed inside. The request and response objects are wrapped in simple wrapper classes to avoid introspection problems that may occurr in some servlet container implementations. You can use the request and repsponse objects normally, accessing methods of either from the template. Just note that they aren't specifically javax.servlet.XXXX classes, if that is important to you.
void setContentType( HttpServletRequest,HttpServletResponse ) 允许你检查请求,并设置内容类型(content type)。默认的类型是根据Velocity.properties中规定的。如果没有在properties文件中设定,或者说在默认情况下,"text/html"将是内容类型。Allows you to examine the request and set the content type yourself, depending on the request or client. The default implementation sets the content type to be that either specified in the velocity.properties, if any, or the default, "text/html" if not specified in the properties.
void mergeTemplate( Template, Context, HttpServletResponse ) 允许你创建输出流。VelocityServlet使用了一个很有效的Writer类的缓存池,所以在一般情况下,该方法不会被覆盖。Allows you to produce the output stream. The VelocityServlet uses a pool of very efficient Writer classes, so this would usually be overridden in special situations.
void requestCleanup( HttpServletRequest, HttpServletResponse , Context ) 允许你在请求处理结束后做一些清理工作或者资源的回收工作。默认的方法没有做任何事情。Allows you to do any cleanup or resource reclamation at the end of the request processing. The default does nothing.
protected void error( HttpServletRequest, HttpServletResponse, Exception ) 如果在请求处理中出现异常,就将调用该方法来处理异常。默认的处理将会返回一个普通的带有栈信息和错误信息的HTML页面给用户。覆盖该方法提供更人性化的错误提示或者异常处理。Error handler that is called an exception occurrs in request processing. Default implementation will send a simple HTML message with stacktrace and exception information back to the user. Override for custom client messages and more advanced problem handling.
更多的信息请参见Javadoc API文档。For further information, please see the Javadoc API documentation.
部署Deployment
当你部署基于Velocity的servlet应用,你试图确定你的配置文件被用来配置了Velocity引擎。在Tomcat下,达到该目的的方法之一是将你的velocity.properties文件放在你的web应用的根目录下(webapps/appname),并在你的WEB-INF/web.xml文件后面加上:When you deploy your Velocity-based servlets, you will certainly want to ensure that your properties file is used to configure the Velocity runtime. Under Tomcat, one way to accomplish this is by placing your velocity.properties file into the root directory of your web app (webapps/appname ) and then add the following to your WEB-INF/web.xml file : <servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.foo.bar.MyServlet</servlet-class>
<init-param>
<param-name>properties</param-name>
<param-value>/velocity.properties</param-value>
</init-param>
</servlet>
假设一切正常,在MyServlet被加载的时候,会使用velocity.prooetries文件来初始化Velocity引擎。Assuming all is right, this will ensure that when MyServlet is loaded, it will use the velocity.properties file to initialize itself rather than relying on it's internal defaults.
注意Velocity在它的核心的Runtime类中使用的是单例模式,所以,把velocitypXX.jar放在WEB-INF/lib文件夹中会是一个更好的选择。Note that Velocity uses a singleton model for it's central core Runtime class, so it is a very good idea to put the velocity-XX.jar into the WEB-INF/lib directory in all web applications that use Velocity to ensure that the web app classloader is managing your Runtime instance, rather than putting it in the CLASSPATH or the top level lib directory of the servlet runner.
这种部署方式能确保在不同的web应用中使用的Velocity配置不会冲突。This deployment method will ensure that different web applications will not be subject to Velocity configuration conflicts. 在普通的应用中使用Velocity
Using Velocity In General Applications
Velocity被设计为一个通用的工具,在一般的应用中,Velocity也很有用。概括地说,在一般的应用中,使用Velocity的基本结构和在本指南开始介绍的那个结构一致,和VelocityServlet提供给了编写Servlet的便利性一样,Velocity也为一般的应用提供了一些工具类。作为一个一般应用的程序员,你的一个新的责任就是初始化Velocity运行时引擎(Velocity runtime engine),但这也很简单。As Velocity was designed to be a general-use tool, it is just as useful in general application programs as it is servlets. In general, you can use the same programming pattern discussed at the beginning of this guide, but there are a few utility methods provided for application use, just like we provide the VelocityServlet base class for ease of use in servlet programming. The only new responsibility you have as the application programmer is to initialize the Velocity runtime engine, but that is easy.
The Velocity Helper Class
Velocity包含了一个为一般应用准备的工具类,叫做Velocity(org.apache.velocity.Velocity)。提供该类的目的在于提供一些初始化Velocity必须的方法和一些有用的日常工具方法,让使用Velocity更加方便。这个类在Javadoc中有详细记录,如有需要,请参考该文档。在这里,该文档只是作为一个指南,而Javadoc则提供了最完整的API的信息。Velocity contains an application utility class called Velocity ( org.apache.velocity.app.Velocity ). The purpose of this class is to provide the necessary methods required to initialize Velocity, as well as useful utility routines to make life easier in using Velocity. This class is documented in the project's javadoc, so please look there for definitive details. This documentation is intended to be of a tutorial nature; therefore for compete API information, the Javadoc is the definitive source.
Velocity运行时引擎是一个单例的实例,在一个JVM中为所有的Velocity用户提供资源,日志记录,和其他的服务。不过,运行时引擎只需要初始化一次即可。你可能会尝试多次初始化该引擎,但只有第一次的初始化操作是成功的,之后的尝试会直接被忽略。现在的Velocity工具类提供了5个方法来配置运行时引擎。The Velocity runtime engine is a singleton instance that provides resource, logging and other services to all Velocity users running in the same JVM. Therefore, the runtime engine is initialized only once. You can attempt to initialize Velocity more than once, but only the first initialization will apply. The rest of the attempts will be ignored. The Velocity utility class currently provides five methods used in configuration of the runtime engine.
这5个方法分别为:The five configuration methods are :
setProperty( String key, Object o )
给属性key赋值o。该值一般为一个String,但在特殊的情况下,可以是用逗号分隔了的列表值(在一个String中,比如:"foo,bar,woogie")或者其他的一些东西。Sets the property key with the value o. The value is typically a String, but in special cases can also be a comma-separated list of values (in a single String, ex."foo, bar, woogie") as well as other things that will arise. Object getProperty( String key )
返回属性key的值。注意你必须强制的转化类型,不然,返回的总是String。Returns the value of the property key. Note that you must be aware of the type of the return value, as they can be things other than Strings. init()
使用分发包中的属性初始化运行时状态。(这些值在下面的关于属性小节中列出)Initializes the runtime with the default properties provided in the distribution.(These are listed below in the section pertaining to properties.) init( Properties p )
使用参数中传入的java.util.Properties对象中包含的属性来初始化运行时状态。Initialize the runtime with the properties contained in the java.util.Properties object passed as an argument. init( String filename )
使用参数中传入的文件路径对应的属性文件来初始化运行时状态。initilizes the runtime using the properties found in the properties file filename
注意,不论使用哪种方法,默认的属性都会作为基础的配置,所有增加的或者修改了的属性都会覆盖对应的默认的配置。没有被覆盖的属性仍然起作用。这样的好处是只有你关注的属性才被指定,而不需要把所有的属性都指定出来。Note that in each case, the default properties will be used as a base configuration, and any additional properties specified by the application will replace individual defaults. Any default properties not overwritten will remain in effect. This has the benefit that only the properties you are interested in changing need to be specified, rather than a complete set.
另一个需要注意的是init()方法可以被多次调用但并不会出现错误。但是,只有第一次调用init()方法才将使用配置属性集来配置引擎,而其后对配置属性的更改或者重新调用init()方法都会被忽略。Another thing to note is that the init() calls may be called more than once without harm in an application. However, the first call to any of the init() functions will configure the engine with the configuration properties set at that point, and any further configuration changes or init() calls will be ignored.
最普通的初始化Velocity的方法会像是这样:The most common approaches to initializing Velocity will be something like :
用类似于org/apache/velocity/runtime/defaults/velocity.properties文件的格式那样将自己的需要的配置属性的值写入一个文件,或者放入一个java.util.Properties中,并且调用init(filename)或者init(Properties)方法。Setup the configuration values you wish to set in a file in the same format as org/apache/velocity/runtime/defaults/velocity.properties (the default set), or in a java.util.Properties, and then call either init( filename ) or init( Properties ) 使用setProperty()方法独立的设置配置属性,并且调用init()方法。这个方法一般在比较高级的情况下------应用拥有自己的配置管理系统,比如应用使用它自己生成的配置文件在运行时配置Velocity。Set the configuration values individually using setProperty() and then call init(). This method is generally used by more advanced applications that already have their own configuration management system - this allows the application so configure Velocity based upon values it generates at runtime, for example.
一旦运行时被初始化,你可以做你想做的任何事情了,比如把模版合成并放入一个输出流中,Velocity提供的工具类能帮你很简单的完成这些任务。下面是一些该工具类的主要的方法的描述。Once the runtime is initialized, you can do with it what you wish.. This mostly revolves around rendering templates into an output stream, and the Velocity utility class allows you to do this easily. Currently, here are the methods and a brief description of what they do :
evaluate( Context context, Writer out, String logTag, String instring )
evaluate( Context context, Writer writer, String logTag, InputStream instream )
这些方法将使用你提供的上下文来解释输入流---以String或InputStream对象存在,并放入一个输出Writer中。这是一个很方便的用字符串替换标识的方法,如果你坚持使用VTL'模版'-----即使这些'模版'是保存在数据库中的,或者非文件形式的甚至直接是应用生成的。These methods will render the input, in either the form of String or InputStream to an output Writer, using a Context that you provide. This is a very convenienient method to use for token replacement of strings, or if you keep 'templates' of VTL-containing content in a place like a database or other non-file storage, or simply generate such dynamically. invokeVelocimacro( String vmName, String namespace, String params[], Context context, Writer writer )
允许直接使用Velocity宏。这也能通过上面介绍的evaluate()方法达到。这里,你需要指定你想要执行的宏的名字,创建一个传递给VM的参数的数组,一个带有数据的上下文对象和一个输出Writer。注意传递给VM的参数必须是在上下文中对应的值得key值,而不是该宏真正要使用的参数值。就是说,宏真正得到的参数是通过传入的key来从上下文中得到的值。这一个特性可能会在以后改变。Allows direct access to Velocimacros. This can also be accomplished via the evaluate() method above if you wish. Here you simply name the vm you wish to be called, create an array of args to the VM, a Context of data, and Writer for the output. Note that the VM args must be the 'keys' of the data objects in the Context, rather than literal data to be used as the arg. This will probably change. mergeTemplate( String templateName, Context context, Writer writer )
方便的调用Velocity的模版处理和合成服务的方法。该方法会处理并合成模版。更进一步的,该方法也会通过对文件资源加载器设置的属性来加载模版,并且提供Velocity的文件和模版的缓存器。这是最有效的访问模版的方法,除非你有特殊的需求,我们建议你使用该方法。Convenient access to the normal template handling and rendering services of Velocity. This method will take care of getting and rendering the template. It will take advantage of loading the template according to the properties setting for the file resource loader, and therefore provides the advantage of file and parsed template caching that Velocity offers. This is the most efficient way to access templates, and is recommended unless you have special needs. boolean templateExists( String name )
测试一个名字为name的模版文件是否能在当前配置的资源加载器中找到。Determines if a template name is able to be found by the currently configured resource loaders.
一旦我们了解了这些基本的助手方法,我们就能很容易的使用Velocity写出Java程序了,如下面所示:Once we know about these basic helpers, it is easy to write Java program that uses Velocity. Here it is:
import java.io.StringWriter;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.VelocityContext;public class Example2
{
public static void main( String args[] )
{
/* first, we init the runtime engine. Defaults are fine. */Velocity.init();/* lets make a Context and put data into it */VelocityContext context = new VelocityContext();context.put("name", "Velocity");
context.put("project", "Jakarta");/* lets render a template */StringWriter w = new StringWriter();Velocity.mergeTemplate("testtemplate.vm", context, w );
System.out.println(" template : " + w );/* lets make our own string to render */String s = "We are using $project $name to render this.";
w = new StringWriter();
Velocity.evaluate( context, w, "mystring", s );
System.out.println(" string : " + w );
}
当我们运行这个程序,并且testtemplate.vm也在应用的同一个目录下(因为我们这里使用的是默认的资源加载器,会在当前目录下加载模版文件),我们的输出应该是:When we run this program, and have the template testtemplate.vm in the same directory as our program (because we used the default configuration properties, and the defaul place to load templates from is the current directory...), our output should be : template :
Hi! This Velocity from the Jakarta project.
string : We are using Jakarta Velocity to render this.
testtemplate.vm的内容是:where the template we used, testtemplate.vm, is
Hi! This $name from the $project project.
这就是在普通应用中使用Velocity~注意我们没有必要在一个应用中同时mergeTemplate()和evaluate()方法。在这里同时使用它们是为了演示的需要。你可能只需要其中一个,但也要看你应用的需求来选择。That's all there is to it! Note that we didn't have to use both mergeTemplate() and evaluate() in our program. They are both included there for demonstration purposes. You will probably use only one of the methods, but depending on you application requirements, you are free to do what you wish.
这段代码和我们在指南开始时提到的基础结构有一些区别。第一,你创建了一个上下文对象并使用需要的数据填充,但在这个例子中,mergeTemlate()方法被使用了,mergeTemplate()方法为你处理了模版并且合成了输出------使用的正是对底层的Runtime类的调用。在第二个例子中,你是通过一个String对象作为模版的,所以,代码忽略了寻找模版的步骤,并且evaluate()调用底层方法为你合成了输出。This appears to be a little different from the 'fundamental pattern' that was mentioned at the beginning of this guide, but it really is the same thing. First, you are making a context and filling it with the data needed. Where this examples differs is that in the part of the above example where mergeTemplate() is used, mergeTemplate() is doing the work of getting the template and merging it for you, using the lower-level calls in the Runtime class. In the second example, you are making your template dynamically via the String, so that is analgous to the 'choose template' part of the process, and the evaluate() method does the merging for you using lower level calls.
So the example above sticks to the same simply pattern of using the Velocity template engine, but the utility functions do some of the repeated drudge work, or allow you other options for your template content other than template files.
异常Exceptions
在解析和合成的过程中,会有三种异常可能被抛出。这些是除了IO错误之外的错误,在org.apache.vleocity.exception包中可以找到它们:There are three exceptions that Velocity will throw during the parse / merge cycle. This are additional to the exceptions that will come from IO problems, etc. They are found in the package org.apache.velocity.exception and are:
ResourceNotFoundException
当资源管理系统不能找到一个请求的资源(模版)的时候抛出。Thrown when the resource managment system cannot find a resource (template) that was requested. ParseErrorException
当在解析一个资源(模版)发现语法错误的时候抛出。Thrown when a VTL syntax error is found when parsing a resource (template). MethodInvocationException
当在合成时期,上下文中一个对象的方法执行期间抛出异常时抛出。该出错方法抛出的错误将会被包装并传递给应用。这要求你在运行时处理自己的对象抛出的错误。Thrown when a method of object in the context thrown an exception during render time. This exception wraps the thrown exception and propogates it to the application. This allows you to handle problems in your own objects at runtime.
一旦以上的情况发生,一个错误信息就会被放入运行时日志中。更多信息请参见Javadoc API文档。In each case, a message is put into the runtime log. For more information, see the Javadoc API documentation.
其他细节Miscellaneous Details
尽管上面的例子使用的是默认的配置,但是要使用自定义的配置也是很容易的事情。你需要做的就是创建一个属性文件,并将该文件的路径名通过init(String)方法或者通过创建一个自定义属性名/值的java.util.Properties对象并调用init(Properties)方法既可以了。后者更为方便,因为你既可以通过使用load()方法从某个属性文件中加载属性,也可以在运行时动态的从你的应用或者框架的配置文件中得到。这给了你将关于应用的所有属性都配置在一个文件中的灵活性。While the above example used the default properties, setting your own properties is very simple. All you have to do is make a properties file somewhere and pass the name of that file to the init(String) method of the Velocity utility class, or make a java.util.Properties object, add the desired properties and values, and pass that to the init(Properties) method. The latter method is convenient, because you can either fill it directly from a separate properties file via the load() method, or even better, you can fill it dynamically from your own application / framework's property set at runtime. This gives you the freedom to combine all of the properties for your app into one properties file.
如果我们想使用一个不同的文件夹来存放模版文件,我们就可以象这样来加载它们:If we wanted to use a different directory than the current directory to load our template from, we could do something like this :
...import java.util.Properties;
...public static void main( String args[] )
{
/* first, we init the runtime engine. */Properties p = new Properties();
p.setProperty("file.resource.loader.path", "/opt/templates");
Velocity.init( p );/* lets make a Context and put data into it */...
现在,假设你有一个/opt/templates文件夹,并且有一个叫做testtemplate.vm的模版文件在里面,那么一切都将正常工作。如果你这样做后发生了一些错误,那么请确认velocity.log中的记录来得到更多的信息------错误消息能很快地确定哪里出错了。And, assuming you have a directory /opt/templates and the template testtemplate.vm is in there, then things would work just fine. If you try this and have a problem, be sure to look at the velocity.log for information - the error messages are pretty good for figuring out what is wrong. 应用属性
Application Attributes
应用属性是名/值对,在RuntimeInstance中被访问。Application Attributes are name-value pairs that can be associated with a RuntimeInstance (either via the VelocityEngine or the Velocity singleton) and accessed from any part of the Velocity engine that has access to the RuntimeInstance.
相关推荐
《Velocity Java开发指南》中文版是一本专注于介绍Velocity这一开源模板引擎的书籍。Velocity是一个用于生成静态或动态内容的模板解决方案,尤其适用于Java开发者。它能够帮助开发者将静态页面元素与动态数据相结合...
### Velocity Java开发指南知识点概述 ...综上所述,《Velocity Java开发指南中文版》为读者提供了全面的 Velocity 使用教程,涵盖了从基础到高级的所有主题。无论是初学者还是有经验的开发者都能从中受益匪浅。
提供的 "Velocity1.4java开发指南中文版.pdf" 和 "Velocity1.4柾斅巊梡巜撿拞暥斉拞暥斉.pdf" 应该是 Velocity 1.4 的中文版开发文档和使用教程。通过阅读这两份文档,你可以深入学习如何配置 Velocity,如何编写...
Velocity 是一处基于java 语言的模板引擎, 使用这个简单、功能强大的开发工具,可以很容易的将数据对象灵活的 与格式化文档组装到一起;希望本文能指引使用velocity 在开发基于servlet 或一般java 应用程序的应用上...
** Velocity 模板使用指南中文版 ** Velocity 是一个基于 Java 的开源模板引擎,它允许开发者将业务逻辑与页面展示分离,使得网页设计者可以专注于页面的布局和样式,而程序员则关注于程序的逻辑处理。Velocity 在 ...
本文档是 Velocity 模板使用指南中文版,旨在帮助开发人员快速掌握 Velocity 的使用。 Velocity 的优点 ---------------- 相比于 JSP,Velocity 具有以下优点: * 便于维护:Velocity 的模板语言易于理解和维护...
### Velocity中文学习指南 #### 一、Velocity简介与应用场景 **Velocity** 是一款基于 Java 的模板引擎 (template engine),它让非技术背景的用户能够轻松地利用模板语言引用由 Java 代码定义的对象。这种能力使得...
- **《Velocity Web应用开发指南中文版》**:聚焦于如何使用Velocity构建Web应用的具体方法和技术细节。 - **《VTL语法参考指南中文版》**:提供关于Velocity模板语言(VTL)的详尽语法说明和示例。 - **《DB4O中文...
VelocityWeb应用开发指南中文版
"Velocity中文指南" Velocity是一种基于Java的模板引擎,允许Web页面设计者引用Java代码预定义的方法。它可以用来从模板产生Web页面、SQL、PostScript以及其他输出。Velocity将Java代码从Web页面中分离出来,使站点...
总的来说,Velocity作为一个强大的模板引擎,为Java开发者提供了便利的手段来生成动态内容,提高开发效率和代码的可维护性。通过深入理解和熟练运用Velocity,开发者可以构建出高效且易于维护的Web应用程序。
这个压缩包包含了四本关于Velocity的经典中文教程,分别是《Velocity模板使用指南中文版》、《VTL语法参考指南中文版》、《Velocity Web应用开发指南中文版》和《velocity Java开发指南中文版》。以下是对这些教程...