《Velocity java开发指南》中文版

本文详细介绍 Velocity 模板引擎的基础使用方法,包括环境搭建、依赖资源、上下文管理、在 Servlet 中的应用以及在普通 Java 应用中的使用技巧。
1.开始入门
Velocity是一基于java语言的模板引擎,使用这个简单、功能强大的开发工具,可以很容易的将数据对象灵活的与格式化文档组装到一起;希望本文能指引使用velocity在开发基于servlet或一般java应用程序的应用上快速起步。
1.Getting Started
取得Velocity并在你的机器上开始运行很容易,以下是全部详细的说明:
取得Velocity发布版本,go here。
目录及文件说明:
Velocity-X.jar 完整的velocity jar包一般命名格式为velocity-X.jar,其中X是当前版本号。注意这个jar包不包含Velocity所必须依赖的其它jar包(具体见后)。
SRC:完整的源文件代码目录
Examples. 完整的aplication或web App例子。
docs :Veocity文档目录
build: 使用ant编译源码时所需的lib.
OK,现在就可以开始使用了.请将Velocity-x.jar放到你的classpath中或webapp的lib下。
当然,我们强烈建议你先运行其中的例子,以感受Velocity的优异之处.
2.Dependencies 依赖资源
Velocity可运行于JDK1.4或JRE1.4及其以上版本.
Velocity也依赖于其它一些jar包,在分发版本的 build/lib 有,如果你下载的是二进制分发版本,需要到以下地址下载其它依赖包.
Jakarta Commons Collections – 必须.
Jakarta Avalon Logkit – 可选,但强列建议加上,以便输出日志信息.
Jakarta ORO – 可选,仅当用到org.apache.velocity.convert.WebMacro template 这个模板转换工具时.
2.参考资源:
一些优秀的资源和例程列表如下:
开发者邮件列表 mail-lists.
邮件档案表 : http://www.mail-archive.com是很好的一个资源库.可以以’Velocity’为关键字进行搜索。
源代码(源码分发版本) : src/java/... : 含有Velocity project的所有源码
应用程序例程1 : examples/app_example1 : 一个很简单的示例如何在一般应用程序中使用Velocity.
应用程序例程1 2 : examples/app_example2 : 如何在应用程序中使用Velocity工具类.
servlet example : examples/servlet_example1 :示例如何在servlet中用Velocity 输出模板.
logger example : examples/logger_example : 如何定制Velocity的日志工具.
XML example : examples/xmlapp_example : 使用 JDOM 从 Velocity 模板读取内容. 还包含一个递归调用宏的示例.
event example : examples/event_example : 在Velocity 1.1 中使用事件处理API。
Anakia application : examples/anakia : 示例用stylesheet 美化 xml 数据。
Forumdemo web app : examples/forumdemo : 一个基于servlet的论坛功能实现示例.
templates : test/templates :全面展示VTL(Velocity Template Lanauage)功能的模板集合。
context example : examples/context_example : 两个示例如何重写(继承) Velocity context 功能的例子(针对高级用户).

3.它是如何工作的?
1.基本使用模式
在application program或servlet中使用Velocity中,一般通过如下步骤:
对于所有应用,第一步是要初始化Velocity, 一般使用唯一实例模式(Singleton),如Velocity.init().
创建一个Context object.
将你的数据对象加入到Context对象中.
使用Velocity选择一个模板.
合并模板和数据导出到输出流.
下面的代码,通过使用org.apache.velocity.app.Velocity的单实例模式,合并输出:

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对象
VelocityContext context = new VelocityContext();
//向context中放入要在模板中用到的数据对象
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所必须的步骤. 但你可能不想这样按部就班的编写代码 –Velocity提供了一些工具以更容易的方式在servlet或应用程序中使用。在这个指南的后面, 我们将讨论在servlet和普通应用程序中更好的用法.

4.单实例还是多实例(To Singleton Or Not To Singleton...)?
1.Singleton Model
这是系统默认的模式, 这样在jvm(应用程序)或web aplication(一个web程序)中只存在一个Velocity engine实例共享使用。. 这对于配置和共享资源来说非常方便. 比如, 这非常适合用于支持 Servlet 2.2+ 的web application中,每一个web app持有它自己的唯一Velocity实例, 它们可以共享templates, a logger等资源. Singleton可以直接通过使用org.apache.velocity.app.Velocity 类, 如下例子:
import org.apache.velocity.app.Velocity;
import org.apache.velocity.Template;

/*
* Configure the engine - as an example, we are using
* ourselves 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");

2.Separate Instance
在1.2版本以后, 可以在 JVM (or web application.)创建,配置,使用多个Velocity实例;当你希望在同一程序中,对每个实例独立配置时它们的 template directories, loggers等资源时,这是非常方便的. 多实例化时,我们要用到 org.apache.velocity.app.VelocityEngine类. 下面是一个例子,请注意和上面singleton example同法时的不同:
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 using
* ourselves as a logger (see logging examples..)
*/

ve.setProperty( VelocityEngine.RUNTIME_LOG_LOGSYSTEM, this);

/*
* initialize the engine
*/

ve.init();

...

Template t = ve.getTemplate("foo.vm");
可以看到,这是非常简单的直接使用就行了.无论用singleton 或 separate instances都不需要改变你程序的上层结构及模板内容.
对于开发人员而言, 你应在以下两个类中二者择一的使用
org.apache.velocity.app.Velocity 应用于 singleton model, org.apache.velocity.app.VelocityEngine一般用于non-singleton model ('separate instance').


5. The Context
1.The Basics
'context' 是Velocity中的一个核心概念, 这是一个从系统的”数据容器(a container of data)”引出的一个常见概念. 这里的context在java程序层和模板视图层(template layer ( or the designer ))之间扮演着一个”数据对象传送者”'(carrier')的角色.
做为程序员,你可以将你程序生成的不同类型的数据对象放入context中,对于视图设计来说,这些对象(包含它们的数据域和命令)将在模板元素中被引用到(references)。一般来说,你将和视图设计者一起决定应用需要哪些数据,可以说,你放入context中的数据对象在这里成为一种”API”,由视图设计者在模板中来访问.因此,在向context中决定放放哪些数据对象时,程序的设计者需要仔细分析视图表现所需的数据内容。
虽然Velocity中你可以创建自己的Context类来支持一些个性化的应用(比如,一个访问,保存LDAP Server服务的context),你可以实现VelocityContext这个己封装较为完务的基类。
VelocityContext对象基本上可满足大多的应用, 我们强烈建议你除非在特别的情况下,否则不要创建自己的Context实现!
VelocityContext用法十分简单,类似于Hashtable class.下面是这个接口提供的两个基本用法:
public Object put(String key, Object value);
public Object get(String key);
很像Hashtable吧,这里的value必须是一个java.lang.Object类(不能是原始类型,像int,boolean), 也不能是null值. 原始类型(Fundamental types like int or float)必须被包装为一个适当对应的Object型.
OK,以上就是context 对象的用法概念,很简单我们却哆嗦这么:). 关于其更多的介绍,请见API documentation.
2.在模板中用#foreach指令支持迭代对象
在放入context前,你对对象有着全面的操作自由. 但就像所有的自由一样, 你必须遵守一些规则,承担一些责任,因此,你必须理解Velocity是如何使用对象的,Velocity的VTL支持多种类型的集合类型(collection types) 使用#foreach().
Object [] 一般对象数组. Velocity将内功能会将它包装成功之为一个实现Iterator interface对象, 这个转换是不需要程序员或视图设计者参与.
java.util.Collection :Velocity会使用他们的标准iterator() 得到一个可以迭代中使用的 Iterator对象,如果你使用自己的实现了Collection interface的对象,要确保它的iterator() 命令返回一个可用的Iterator.
java.util.Map接口对象,Velocity 使用其顶层接口的values() 命令得到一个实现 Collection interface的对象, 应用其iterator()再返回一个Iterator.
java.util.Iterator使用特别注意 : 如果一个Iterator对象被放置到context中,当在模板中有多个 #foreach()指令中,这些#foreach() 将顺序执行,如果第一个调用失败,后面的将阻塞且不能重置.
java.util.Enumeration USE WITH CAUTION : 如同java.util.Iterator一样的道理,Velocity将使用的是一个不能重置('non-resettablity')或者说一个final型的对象.
因此,仅当在不得己的情况下,Iterator and Enumeration 对象才有必要放入context中---也许你有更好的办法不使用他们.
例如,你可以将如下代码:
Vector v = new Vector();
v.addElement("Hello");
v.addElement("There");

context.put("words", v.iterator() );

替换为:
context.put("words", v );

3.Context Chaining
另外一个新引入的概念是context chaining.有时也叫做context wrapping(有点类似与servlet中的chain), 这个高级特性让你可以连结多个独立的Velocity的contexts,以便在template中使用.
以下是这种用法的代码示例 :
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 );
在上面的代码中, context2 做为context1的chains. 这意味着你在模板中可以使用放入这两个context中的任何一个对象, 当两个context中有相同的key中,在模板中输出时,将会输出最后一个key的值,如上例key为duplicate的值将输出为 "I am in context2".
其实,在上例中不存在duplication, 或'covering', context1中的string "I am in context1" 依然可以通过context1.get("duplicate")方法得到. 但在上例中,模板中引用 '$duplicate' 将会返回 'I am in context2', 而且模板不能再访问到context1中的'I am in context1'.
另外要注意的是,当你尝试在模板中加入信息,比如使用#set()声明,这将对所己输出的模板产生影响.
如前所述, Velocity context类也是或扩展的, 但在这份指南中没有述及. 如果你有兴趣,可以查看org.apache.velocity.context 中的代码以了解contexts 是如何生成,java数据对象以何机制传出的. 例程 examples/context_example有一些示例展现.
4.模板中的己创建对象
Java代码中的数据对象与模板交互有两种常见方式:
模板设计者从模板中执行程序员放入到context中的java对象的命令:
#set($myarr = ["a","b","c"] )
$foo.bar( $myarr )
当模板加入一个对象到context中,模板合并输出后,java代码将可以访问这些对象.
#set($myarr = ["a","b","c"] )
#set( $foo = 1 )
#set( $bar = "bar")
这里述及这些技巧有些过早,但现在必须理解以下概念:
The VTL通过context或method所传的 [ 1..10 ] and ObjectArray ["a","b"] 是java.util.ArrayList对象. 因此你的对象的命令设计时,要具有兼容性.
Numbers在context中将被包装为Integers, strings,当然就是Strings了.
Velocity会适当的根据调用的参数类型适配对象的调用命令, setFoo( int i )将一个 int 放入context 和 #set()是不会冲突的.
5.Context对象的其它用法
每一个VelocityContext(或任意源自AbstractContext)的对象,都是一个封装好指定规则的的存储节点,对于一般开发都来说,只需使用就是.但这里还有一些你应知道的特性:
考虑以下情况:
你的模板重复使用VelocityContext object.
Template caching is off.
反复调用getTemplate() 命令.
这都有可能引起 VelocityContext的内存泄露( 'leak' memory )---当它汇集过多的数据对象时,因此强烈建议你做到以下几点 :
在每个模板渲染过种中(template render process)创建一个新的VelocityContext. 这会防止过多的cache data. 当需要重用一个 VelocityContext 因为它内部己放置了数据对象, 你只需要像这样简单的包装一下:VelocityContext useThis = new VelocityContext( populatedVC );具体可以参看 Context chaining 获取更多信息.
打开模板的caching功能. 以防止重复解析模板,当然,这会要求服务器有更高的性能.
在迭代操作时,要重用模板对象. 这样将不会对Velocity造成过大压力, 如果缓存关闭, 就需要每次都读取和解析模板, 导致 VelocityContext 中每次都要保存大量新的信息.

6.Using Velocity In Servlets
1.Servlet Programming
Velocity最通常用在servlet中做为www服务. 有非常多的理由告诉你这项任务是最适合Velocity完成的,最重要的一个就是Velocity's可以分离视图(表现层和)代码层.在这里可以看到更多的理由this.
在servlet中使用Velocity是非常简单的. 你只需要extend 己有的 VelocityServlet class和一个必须实现的方法: handleRequest().
public Template handleRequest( HttpServletRequest, HttpServletResponse, Context )
这个方法直接传送HttpServletRequest 和 HttpServletResponse objects,. 这个方法可以返回null值表示所有处理己经完成, 相对而言,指示velocity调用velocity requestCleanup()更为常用. 如下代码示例(在例程中有)
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;
}
}


看起来好熟悉吧? 除过处理一些异常,基本的功能Velocity都己为你准备好了, 就连在应用程序中要你写的merge() 这一步,VelocityServlet自己也己处理,这就是基本的用法:取得context, 加入我们自己的对象最后返回模板 template.
默认的 Context是作为 handleRequest() 传入的. 可以使用以下常量直接访问 request和response对象,VelocityServlet.REQUEST (value = 'req') and VelocityServlet.RESPONSE (value = 'res') ,如下是java例程 :
public Template handleRequest( Context context )
{
HttpServletRequest request = (HttpServletRequest) context.get( REQUEST );
HttpServletResponse response = (HttpServletResponse) context.get( RESPONSE );

...
}
可以在模板中如下访问:
#set($name = $req.getParameter('name') )

一些更高级的用法,如VelocityServlet base class 可以在处理请求时重写更多的签名方法 :

Properties loadConfiguration( ServletConfig )
这可以重写常规的配置方法. 这常用在修改日志路径或运行时改写webapp root的绝对路径.

Context createContext(HttpServletRequest, HttpServletResponse )
你可以创建自己的Context object. 这可以使用更高级的技术, 如数据链或预载数据和工具类. 默认的实理仅返回一个己内置request and response 对象的VelocityContext.你直以在模板中直接访问他们的命令。

void setContentType( HttpServletRequest,HttpServletResponse )
你可以自己定义contentType, 或提取client定义的. 默认的contentType类型在velocity.properties文件中配置, 一般来说,默认的"text/html" 类型是不够详细的.

void mergeTemplate( Template, Context, HttpServletResponse )
你可以生成输出流(output stream). VelocityServlet使用含有多种输出对象的池, 在特定情形下,重写这个命令是有用的.

void requestCleanup( HttpServletRequest, HttpServletResponse , Context )
这个调用一般在处理完后做资源的清理工作,如有需要,在这个命令的重写内容中加上你的代码.

protected void error( HttpServletRequest, HttpServletResponse, Exception )
在处理中,当错误发生时,这个方法会被调用. 默认的实现是将发送一个简单的HTML格式的错误内容到客户端. 你可以重写以定制错误处理.
更多的信息,请参考 API documentation.


2.Deployment
发布基于Velocity的servlets 时,需要参数来配置 Velocity runtime. 在Tomcat上, 一个简单的方法是将velocity.properties文件放到你的webApp root目录下 (webapps/appname ) 然后在 WEB-INF/web.xml 文件中加上以下几行 :
[quote]<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>[/quote]

以上配置将确保MyServlet 可以被载入,Velocity也将会使velocity.properties来初始化.
注意: Velocity 在运行时使用的是单实例模式, 因此将 velocity-XX.jar放到YourWebAPP/WEB-INF/lib目录即可. 但当多个webApp要使用时,放入CLASSPATH 或Servlet容器的顶层 lib是最好的选择.
7. Using Velocity In General Applications
Velocity被设计为一个通用的功具包,它也常用于一般的应用程序中. 如这篇指南开始所讨论的那样, 另外,这里还有一些对一般应用程序来说很有用的工具类:
1.The Velocity Helper Class
Velocity 提供一个 utility class ( org.apache.velocity.app.Velocity ). 这个类中提共一个初始化Velocity所必需的方法, 具体的细节可以到文档中查看.
Velocity runtime engine在运行时是单实便模式,它可以给 Velocity的用户通过同一个jvm提供日志,资源访问方法. 这个engine运行时仅初始化一次. 当然,你可以尝初对init()进行多次调用, 但只有第一次有效. The Velocity utility class 目前提供5个命令来配置运行时engine:
这5个配置方法是 :
setProperty( String key, Object o )
这么简单,不用解释了吧.
Object getProperty( String key )
嗯,也不说了
init()
用默认的参数文件初始化
init( Properties p )
用一个特定的java.util.Properties对象初始化.
init( String filename )
用指定名字的参数文件初始化
注意:除非你指定了,否则初始时会使用默认参数. 仅当在调用init()时的配置会对系统生效。
通用的 initializing Velocity 一般步骤如下:
设置你指定的配置参数值在文件org/apache/velocity/runtime/defaults/velocity.properties中,或放入java.util.Properties, 在第一次调用init( filename )或 init( Properties ) .
单独设置每个配置参数通过调用 setProperty(),最后调用init(). 这适合一些一有自己的CMS系统(configuration management system )程序。
运行时一但初始化, 你就可以开始工作了.. 只需要考虑组装什么样的数据对象到你的输出模板上, Velocity utility class 可以帮你更容易做到这些.这里有一些命令摘要描述如下 :

evaluate( Context context, Writer out, String logTag, String instring )
evaluate( Context context, Writer writer, String logTag, InputStream instream )
这个命令用来修饰输入流,你可以将包含TVL的模板文件从内置的String对象、DB、或非文件系统的数据源输入,.
invokeVelocimacro( String vmName, String namespace, String params[], Context context, Writer writer )
你可以直接访问Velocimacros. 也可以通过evaluate()命令来完成,这里你只需简单的传入VM文件名(模板文件), 创建一组VM参数放到Context,然后输出. 注意放入Context的参数必须是键-值对出现的.
mergeTemplate( String templateName, Context context, Writer writer )
这个命令用来执行Velocity的模板合并和渲染功能. 它将应用context中的数据对象到指定文件名的模板中.将结果输出的指定的Writer. 当然,如果没有特别的需要,不建议这么做.
boolean templateExists( String name )
检测当前配置的资源中,是否存在name的模板名.
这样,我们就更容易编写使用Velocity的java代码了. 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 (默认配置中模板文件的输出位置是当前目录):

[quote]template : Hi! This Velocity from the Jakarta project.

string : We are using Jakarta Velocity to render this.

where the template we used, testtemplate.vm, is
Hi! This $name from the $project project.[/quote]

在这里,我们不得不使用 mergeTemplate() and evaluate() in our program. 这主要是为了示例. 在一般应用中,这是很少使用到的,但我们提供了根据具体需要自由使用的途径.
这有点不和我们这篇文张本意的“基础功能指南”这一意图, 但我想这是必须知道的. 首先, 你得到一个放置了数据对象的context对象, 不用的是使用了命令 mergeTemplate(), mergeTemplate() 所做的工作是合并模板, 在运行时调用低层功能(lower-level). 接下来,通过evaluate()方法使用一个String动态生成模板.
这是同样简单的使用Velocity engine的方式,这些可选功能也许可以帮你做一些重复的工作,比如生成模板的模板:)

2.Exceptions
There are three exceptions that Velocity will throw during the parse / merge cycle在解析/合并(parse/merge)模板周期中,Velocity或能出现三个Velocity异常.另外,还可能会有 IO problems, etc. Velocity自定义的异常可以在package org.apache.velocity.exception:
ResourceNotFoundException
当velocity的资源管理器无法找到系统请求的资源时出现.
ParseErrorException
当parse模板文件中的 VTL 语法出错时出现.
MethodInvocationException
Thrown when a method of object in the context thrown an exception during render time,当处理模板中,context中的对象的命令调用出错时.
当然,每一次出错时,相关的消息内容会保存到运行时的日志文件中,获取更多资料,请查看API文档。

3.其它细节
在以上的一些例程中,使用默认的properties配置你的程序非常方便. 但你可以根据自己的需要,用自己的配置文件通过在Velocity中调用init(String yourFileName)命令传入你的配置文件名, 或创建一个保存了你的配置参数的 java.util.Properties对象,通过调用 init(Properties)命令来实现. 这其中后一个方式是很便捷的, 你可以直接将一个独立的参数文件通过load()调用,将其中的配置参数置入Properties对象中. 你还可以做得列好---你可以在运行时运态的从你的程序框架中加入参数. 这样,你应可以将Velocity的配置参数和你原来应用的配置在不做大的更改情况下共用。
当需要从一个指定的目录提取模板文件时(默认为当前目录),你可以这样做 :
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 */

...
这样,Velocity在需要时,将自动到/opt/templates目录下查找模板文件,如果有问题可以查看velocity.log中所记录的出详细出错信息。它会帮你有效的消息错误.
1.开始入门 3 1.Getting Started 3 2.Dependencies 依赖资源 3 2.参考资源: 4 3.它是如何工作的? 4 1.基本使用模式 4 4.单实例还是多实例(To Singleton Or Not To Singleton...)? 6 1.Singleton Model 6 2.Separate Instance 6 5. The Context 7 1.The Basics 7 2.在模板中用#foreach指令支持迭代对象 8 3.Context Chaining 9 4.模板中的己创建对象 10 5.Context对象的其它用法 10 6.Using Velocity In Servlets 11 1.Servlet Programming 11 2.Deployment 13 7. Using Velocity In General Applications 13 1.The Velocity Helper Class 14 2.Exceptions 16 3.其它细节 16 8.Application Attributes 17 9.EventCartridge and Event Handlers(事件分发和处理) 17 1.Event Handlers 17 2.Using the EventCartridge使用事件分发器 18 10.Velocity Configuration Keys and Values(配置参数名字和值说明) 20 1.Runtime Log 20 2.字符集编码问题 21 3.#foreach() Directive 21 4.#include() and #parse() Directive 21 5.资源管理 21 6.Velocimacro(宏配置) 22 7.语义更改 23 8.运行时配置 23 11.Configuring the Log System(日志记录配置) 23 1.一般的可选日志功能: 23 2.Simple Example of a Custom Logger 25 12.Configuring Resource Loaders(资源装载器配置) 26 1.Resource Loaders 26 2.Configuration Examples 27 3.插入定制资源管理器和Cache实现 29 13.Template Encoding for Internationalization(字符编码和国际化) 29 14.Velocity and XML 30 15.FAQ (Frequently Asked Questions) 32 1.Why Can't I Access Class Members and Constants from VTL? 32 2.Where does Velocity look for Templates? 33 16.Summary 33 17.Appendix 1 : Deploying the Example Servlet 33
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值