原文地址 译者:冷风 校对:方腾飞
如果你使用的是VelocityViewServlet或者其他的web框架,你不会直接调用到Velocity。但是,如果在非web的应用中或者自己编写的web框架时,你将会像上面说的基本流程一样直接调用到Velocity 引擎。另外一个需要记住的重点是,必须在使用Velocity合并模版之前,初始化Velocity引擎。
Velocity辅助类
Velocity中包含一个叫做Velocity( org.apache.velocity.app.Velocity )的应用工具类。这个类的主要是提供一些初始化Velocity时必须的方法,以及简化Velocity使用的一些常规方法。这个在工程的javadoc中有描述,可通过阅读javadoc来获取更详细的说明。本篇文档只是教程;所以,如果需要了解完整的API信息,javadoc是你最好的选择。
Velocity运行时引擎是为在同一个jvm中的其他使用者提供资源获取,日志记录等其他服务的单个实例。因此,运行时引擎只会初始化一次。你可以尝试初始化多次,但是只有第一次是有效的,后面的初始化操作都会被忽略。Velocity工具类提供了五个用来配置运行时引擎的方法。
这五个配置方法如下:
- setProperty( String key, Object o )
设置属性键key对应属性值为o。值通常为字符串,在特殊情况下也可以是逗号分隔的一串值(如”foo,bar,woogie”),当然也可以其他值。 - Object getProperty( String key )
获取属性键key对应的值。需要注意的是返回值的类型,不仅仅只是字符串类型 - init()
使用jar包中的默认properties文件中配置的属性初始化运行时引擎 - init(Properties p)
使用类型为java.util.Properties参数出入的属性来初始化运行时引擎 - init( String filename )
使用文件名为filename的properties文件中的属性值初始化运行时引擎
需要注意的是,在上面的这五个方法中,默认的properties是基础的配置,额外的properties是用来替换默认配置中对应的属性。没有被替换的默认属性还将继续发挥作用。这有利于你只需替换你感兴趣的,而不需要整个都替换掉。
另外需要注意的是,init() 方法可以在应用中被调用多次,这并不会带来其他影响。但是只有在第一调用时的引擎配置才会生效,后面调用的init()使用的配置都会被忽略。
最常见的初始化Velocity的方式,通常如下:
1 按照org/apache/velocity/runtime/defaults/velocity.properties 文件中的格式配置你需要替换的属性,放在文件中或者放到java.util.Properties中,然后调用init(filename) 或者 init(Properties)
2 通过setProperty() 方法设置单个的配置项,然后调用initial()。 这种方法通常在拥有自己的配置管理系统的高层应用中。例如,允许应用基于运行时产生的值来配置Velocity
一旦运行时引擎初始化完成,你就可以进行任何你想做的了。当然,主要还是围绕着渲染模板输出内容到输出流中,Velocity辅助类中的一些方法可以帮你轻松搞定,下面是这些方法以及他们做的事情的简要描述:
- evaluate( Context context, Writer out, String logTag, String instring )
- evaluate( Context context, Writer writer, String logTag, InputStream instream )这些方法使用传入的contexxt对象渲染一个输入,输出到Writer中;输入的内容可以是一个字符串或者一个InputStram。这个方法经常用在替换字符串中的标记、渲染模板内容保存在数据库中或其他的非文具的存储中,或者动态生成内容时。
- invokeVelocimacro( String vmName, String namespace, String params[], Context context, Writer writer )
通过这个方法可以直接调用到Velocity宏,也可以通过上面的evalutae()方法调用。你只需要给你的vm模板取一个名字,创建一个提供给vm使用的数组,一个包含数据的context,以及用于输出内容的Writer。需要注意的是这里说的提供给vm使用的数组中的元素只能是context中有的key,而不是其他用作参数传入vm的字符串。这点在后面的版本有可能修改。
- mergeTemplate( String templateName, Context context, Writer writer )通过这个方法可以非常方便的调用到Velocity提供的模板出来和渲染的功能。这个方法会读取和渲染模板。文件加载器会根据你设置的properties的设置来加载模板内容,所以可以Velocity提供的文件处理的优势以及预解析模板缓存的优势。
- boolean templateExists( String name )判断当前配置的资源加载起能否都让渠道名为name的模板文件。
通过上面的文档,我们已经清楚了这些基础的辅助方法,现在就可以地编写使用Velocity的java程序了:
import java.io.StringWriter; import org.apache.velocity.app.Velocity; import org.apache.velocity.VelocityContext; public class Example2 { public static void main( String args[] ) { /* 首先,初始化运行时引擎,使用默认的配置 */ Velocity.init(); /* 创建Context对象,然后把数据放进去 */ VelocityContext context = new VelocityContext(); context.put("name", "Velocity"); context.put("project", "Jakarta"); /* 渲染模板 */ StringWriter w = new StringWriter(); Velocity.mergeTemplate("testtemplate.vm", context, w ); System.out.println(" template : " + w ); /* 渲染字符串 */ 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放到与程序相同的目录下(因为我们使用默认配置,默认配置中指定的模板加载路径就是程序的当前目录)。运行上面代码后,将会输出:
template : Hi! This Velocity from the Jakarta project.
string : We are using Jakarta Velocity to render this.
模板文件testtemplate.vm文件的内容是:
Hi! This $name from the $project project.
好了,上面就是我们使用Velocity渲染模板需要做的事情了。在代码中没有必要既调用mergeTemplate()又调用evaluate(),这里只是为了演示才同时使用了。你一般只需要使用其中一个方法就够了。但是到底调用一个还是两个,需要根据你程序的具体要求确定。
这里看起来与文章最开始描述的 基本流程 有一些差别,但实际是他们是相同的。首先,你还是需要先创建一个context,放进你需要的数据。上面描述的例子中不同的是使用了mergeTemplate()方法,mergeTemplate()方法调用了底层的Runtime类中的方法加载了模板,然后合并内容。在上面的第二个例子中,通过字符串动态创建模板,这个操作就类同于基本模式中的选择模板的操作;后面的merge()方法就调用了底层的方法来合并模板内容。
所以上面的例子跟文章开始描述的 基本流程 是一样,只不过这里的一些工具方法做了那些重复的苦力活;同时也演示了除了从模板文件中获取模板内容外,你也可以动态的创建模板内容。
异常
Velocity在解析和合并模板的过程中可能会抛出异常。这些异常都继承RuntimeException,所以没有显式捕获的必要,每一种异常都包含了特定的属性以提供给最后的调用者来获知异常的具体信息。这些异常类都放在了org.apache.velocity.exception包下,有如下几种:
1. ResourceNotFoundException
当资源管理系统找不到请求的相应资源时抛出。
2. ParseErrorException
在解析Velocity模板语法是出现错误时抛出
3.TemplateInitException
在模板解析的第一个阶段抛出,提示Velocity宏或者指令初始化时出现的错误
4.MethodInvocationException
在渲染模板时调用Context中某个对象的方法是出现错误时抛出。该异常包装了调用对象的方法抛出的异常并传递给应用,让你能够在运行时处理这些对象中的问题。
在每次抛出上面的这些异常时都会记入到运行日志中。可通过阅读javadoc api 文档了解更多详细信息。
其他细节
虽然上面的代码中使用的是默认属性,但是设置自定义的属性也是非常简单的。你需要做的就是创建一个properties文件,在文件中添加你需要自定义的属性,然后传递文件的路径给Velocity工具类中的init(String)方法;或者创建一个java.util.Poperties 对象,往这个对象中自定义的属性,然后传递给Velocity工具类的init(Properties)方法。后一种方式显得灵活些,因为你可以通Properties的load()方法加载一个properties文件,更好的是你应用或框架中动态传入运行时设置的属性。你可以方便的将应用用到的所有属性组合放到一个properties文件中。
如果你想从其他目录而非当前目录中加载模板文件,我们可以通过下面这种方式来完成
... import java.util.Properties; ... public static void main( String args[] ) { /* 首先,我们还是初始化运行时引擎 */ Properties p = new Properties(); p.setProperty("file.resource.loader.path", "/opt/templates"); Velocity.init( p ); ...
为了能顺利运行上面的代码,你需要有一个 /opt/templates 目录,并且把文件testtemplate.vm放到该目录下。如果你按照上面的做了,还是出现问题,可以查看velocity.log文件确定具体原因,毕竟阅读错误日志是你定位问题的不二选择。
... VelocityEngine velocityEngine = new VelocityEngine(); ExtendedProperties eprops = null; if (props==null) { eprops = new ExtendedProperties(); } else { eprops = ExtendedProperties.convertProperties(props); } // 现在,我们用一个对象实例来设置属性 eprops.setProperty("name", object); ... velocityEngine.setExtendedProperties(eprops); velocityEngine.init(); ...
你可以考虑尝试使用下面章节描述的[应用属性]特性。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/118282.html