Velocity是一个表示层的模板框架,作为 jsp 的替代者,有着很多优势。小巧方便,不必使用 el 或者其他表示层技术来展示页面,感觉是一个很新颖的技术。
Velocity作为 MVC 框架中的 V 存在,和普通 jsp 页面有着不同,它是一种 template 引擎,利用先编辑完的格式来作为大纲,把一些需要变化的地方作为参数传入,显示时将模板和参数合并,达到最终输出的样子。本文使用目前最新的 velocity1.6.2.jar 包进行解析。
?
最根本的velocity 就是由 template , engine , context 组成。
1、首先创建一个 template (如果是用在 web 上就是一个 html 文件),将需要参数化或实例化的地方用跟 context 有关的符号标记出来 , 标记时使用 velocity?template?language 。而 template 应该可以是任意的文本。
2、给 context 设定一些值,这些值用来替换在 template 中被标记的地方。
3、利用 engine 将 template 中需要替换的地方用 context 中的值替换掉,也就是所谓的 merge ,从而得到该模板的实例。
?
简单的用java 代码来输出就是:
?
VelocityEngine?velocity?=? new ?VelocityEngine();
VelocityContext?context?=? new ?VelocityContext();
context.put( "name" ,? "czy" );
Template?template?=?velocity.getTemplate( "/src/main/resources/test.vm" );
BufferedWriter ? writer?=? new ?BufferedWriter( new ?OutputStreamWriter(System. out ));
template.merge(context,?writer);
writer.flush();
writer .close();
?
至于我们为什么使用 VelocityEngine? 而不是使用Velocity 类,或者说两者有什么区别,请看 《 VelocityEngine? 和Velocity 类解析 》
?
????我们假设使用普通的velocityEngine 来作为引擎,来进入初始化的过程。
Velocity的初始化有多种方式:
1、 init()
2、 init(Properties?p)
3、 init(String?propertiesName)
?
第一种init() 方式是最简单的方式,当我们的程序没有配置类似 velocity.properties 这样的文件的时候,就会使用默认的配置文件来初始化,默认的配置文件的位置在
org/apache/velocity/runtime/defaults/velocity.properties
而将Velocity 应用在 WEB 的时候,默认的配置文件使用的是
org/apache/velocity/tools/view/servlet/velocity.properties
?
在初始化前,velocity 会把配置文件的属性和值读取后保存在内存中,初始化时, velocity 将会初始化以下几个方面:
?
1、 Logging ? System ?日志系统
2、 ResourceManager ?资源加载器
3、 EventHandler ?事件句柄
4、 Parser ? Pool ?解析池
5、 Global ? Cache ?全局缓存
6、 Static ? Content ? Include ? System
7、 Velocimacro ? System ?宏
?
1、 Logging?System
顾名思义,是进行日志的初始化工作,Veloicty 会使用 LogManager 来默认创建一个 LogChute 的实例。 Velocity 早些时候是使用 LogSystem 来作为日志的接口,而现在则使用 LogChute 。第一个最初最初的默认实例叫做 HoldingLogChute ,其实里面就是一个 Vector 来保存信息,用来作为初始化日志系统的日志实例,呵呵,很拗口。
在创建了第一个系统内置的日志实例后,才会开始真正的创建日志系统,velocity 会根据配置文件里的信息,查找一个名叫 runtime.log.logsystem 的属性,一旦配置文件中有配置这个属性,则会开始去创建日志系统。当velocity 没有配置 runtime.log.logsystem 这个属性的时候,则会继续寻找 runtime.log.logsyste m.class 这个属性,默认的velocity.properties 配置文件中,这个属性的值按顺序依次为
1、AvalonLogChute?
2、Log4JLogChute
3、CommonsLogLogChute
4、ServletLogChute
5、JdkLogChute
当然,velocity 不会使用那么多个日志系统,只会使用第一个能实例化的日志系统。
如果之前的创建工作都失败的话,那么意味着用户没有设置值或者是没有找到类,velocity 将会使用系统统默认的 SystemLogChute 来输出日志,这个日志系统使用 System.err 方式输出日志。
一旦使用 runtime.log.logsystem 或者 runtime.log.logsyste m.class属性创建日志系统成功后, velocity 就会把 HoldingLogChute 替换成新的日志系统。这样,日志的初始化才真正的结束。
?
2、 ResourceManager
资源加载器是velocity 加载资源使用的一个工具。
Velocity的配置文件里有个属性叫 Resource.manager.class 默认的class 是
org.apache.velocity.runtime.resource.ResourceManagerImpl
Velocity会尝试初始化 ResourceManagerImpl ,其中会查找 resource.loader 这个属性, resource.loader 这个属性是可以有多个的,每个Loader 都会生效。
ResourceLoader一共有 7 种:
?
1、 ClasspathResourceLoader
2、DataSourceResourceLoader
3、FileResourceLoader
4、JarResourceLoader
5、StringResourceLoader
6、URLResourceLoader
7、WebappLoader
?
默认的是第3 个 FileResourceLoader 。除了第 7 个 webappLoader 是 velocity-tools 包作为 velocity 的附属工具后来添加的,其余 6 个都 velocity 包自带的, 已经足够满足大多数的需求,当然,如果觉得这些都不适用,你也可以自己实现一个。实现一个资源加载器,实际上很简单,只要继承ResourceLoader ,实现它的几个方法就可以了。
所谓的资源加载器指的就是velocity 读取文件的方法,有直接从文件读取的,有从 jar 包中读取的,也有从类路径中读取的,基本上只要自己重写 getResourceStream 方法就可以。
在初始化的过程中,会读取 resource.manager.logwhenfound 和 resource.manager.cache.class 这两个属性,同时,也会进行资源缓存的初始化操作。
?
3、 EventHandler
Velocity在渲染页面的时候,提供了不同的 EventHanlder ,供开发者 callback 。说白了就是Velocity 渲染页面的不同工具,用户可以自定义响应的事件。 Velocity提供了对模板解析过程事件的处理,用户可以响应模板产生的事件。?
?
org.apache.velocity.app.event.EventHandler,是一个最简单的接口。我们可以通过实现这个接口来处理页面上不同的信息。
?
以下是这个接口的一些扩展接口。
1)、 IncludeEventHandler
在使用#include(),#parse() 语法的时候,允许开发修改 include 或者 parse 文件的路径(一般用于资源找不到的情况) 。
IncludeEventHandler有两个实现类,分别是 IncludeNotFound 和 IncludeRelativePath 。
?
当找不到#include 指令的文件时 ,IncludeNotFound 类会去做一些处理,例如去增加一个 eventhandler.include.notfound = notfound.vm 的配置,当然,如果不存在notfound.vm ,也会给出 " Can't?find?include?not?found?page "的提示。
2)、 InvalidReferenceEventHandler
当渲染页面的时候,一旦遇到非法的reference ,就会触发此事件。开发者可以侦听此事件,用于错误的报告,或者修改返回的内容。
ReportInvalidReferences 是它的一个实现类, 用于报告无效的refenrences 。如果在 velocity 的配置文件中使用了 eventhandler.invalidreference.exception?=?true 配置,在运行过程中碰到第一个无效的refenrences 就会抛出 ParseErrorRuntimeException 异常,执行暂停。如果配置为false 的话,则会将错误先收在 InvalidReferenceInfo 列表对象中,运行照旧。
3)、 MethodExceptionEventHandler
渲染模板,一旦发现调用的方法抛出异常的时候,就会触发此事件。允许开发者处理这个异常,输出友好信息或者抛出异常。必须返回一个值用于模板的渲染。?
4)、 NullSetEventHandler
当使用#set() 语法,设置一个 null 值的时候,会触发此事件。目前 Velocity 官方没有提供默认实现 。
5)、 ReferenceInsertionEventHandler
当渲染变量(reference )的时候,就会触发此事件。允许开发者返回更加友好的值--一般用于内容的 escape ,比如 HtmlEscape 等。
?
4、 Parser?Pool
Velocity会使用类似线程池的机制来解析页面。配置的 key 为 parser.pool.class 和parser.pool.size 。
默认的实现类为 o rg.apache.velocity.util.SimplePoolVelocity 。
velocity 启动 时 需要创建模板解析器的个数.? 默认 为 20个 , 对一般用户来说足够了.? 即使这个值小了, Velocity 也会运行时根据系统需要动态增加 ( 但增加的不会装入 解析 池中).? 新增时会在日志中输出信息 。
?
5、 Directives
所谓的指令指的就是在页面上能用一些类似标签的东西。 V elocity默认的指令文件位置在 org/apache/velocity/runtime/defaults/directive.properties 。
在这个文件中定义了一些默认的指令,例如:
directive.1 = org.apache.velocity.runtime.directive.Foreach
directive.2 = org.apache.velocity.runtime.directive.Include
directive.3 = org.apache.velocity.runtime.directive.Parse
directive.4 = org.apache.velocity.runtime.directive.Macro
directive.5 = org.apache.velocity.runtime.directive.Literal
directive.6 = org.apache.velocity.runtime.directive.Evaluate
directive.7 = org.apache.velocity.runtime.directive.Break
directive.8 = org.apache.velocity.runtime.directive.Define
我们在vm 文件中可以直接使用 foreach 等指令来让我们的页面更加的灵活。
?
5、 Velocimacro(宏配置)
当Velocity?engine 运行时, 会载入一个全局的宏文件。 所有模板都可访问 该 宏 文件 (Velocimacros?).?这个文件位置在相对于资源文件的根目录下 。velocity 默认的配置项为 velocimacro.library?=?VM_global_library.vm 。
此外,还有一些其他配置来处理宏的不同使用情况,例如:
velocimacro.permissions.allow.inline?=?tru e 定义在模板中是否可用#macro() 指令定义一个新的宏 。 默认为true ,表示所有的vm 都可以新建宏,但是要?注意可能会把全局的宏配置给替换掉。
velocimacro.permissions.allow.inline.to.replace.global?=?false 控制用户定义的宏是否可以可以替换Velocity 的 全局宏。
velocimacro.library.autoreload?=?false 控制宏是否自动载入 。当值 为true 时 宏 将根据是否修改而决定是否需要 重新加载,这个特性 可在调试时很方便,不需重启你的服务器 。
?
?
此之外,还有些组件:
Anakia:一个示例应用,该应用允许不使用 xsl 处理 xml 。
Application?servers:对所有主流的 servers 和 servlet 提供了支持,比如有一个 VelocityServlet 类。