简介
Velocity 历史悠久的免费java模板引擎。官网:velocity.apache.org/
Velocity是基于Java的模板引擎,这是一种简单而强大的开发工具,可让您轻松创建和呈现用于格式化和显示数据的文档
*.vm :Velocity 模板文件
VTL : Velocity Template Language
使用Velocity模板引擎时的需要关注两部分:Velocity模板和Java代码调用。
Velocity模板由VTL和引擎上下文对象构成;
Java代码调用部分则负责初始Velocity引擎、构建引擎上下文对象、加载Velocity模板和启动模版渲染。
而Velocity模板与Java代码调用部分通信的纽带就是引擎上下文对象了。
Velocity被移植到不同的平台上,如.Net的NVelocity和js的Velocity.js,虽然各平台在使用和实现上略有差别,但大部分语法和引擎核心的实现是一致的,因此学习成本降低不少哦。
版本要求
Velocity2.1以上版本要求jdk1.8以上
Velocity2.2的maven 依赖
1 | 复制代码<dependency> |
其它所有版本官方下载地址:archive.apache.org/dist/veloci…
Velocity1.7要求jdk1.4版本以上。官网地址:velocity.apache.org/engine/1.7/
1.7 api doc地址:tool.oschina.net/apidocs/api…
快速入门
Example2.java
1 | 复制代码import java.io.StringWriter; |
testtemplate.vm
1 | 复制代码Hi! This $name from the $project project. |
总结
首先,你还是需要先创建一个context,放进你需要的数据。
然后合并内容
Velocity的Java编码
以下内容转自:www.cnblogs.com/fsjohnhuang… 肥仔John
模板与宿主环境通信
模板指的是使用VTL编写的Velocity模板,宿主环境指的是Java代码调用部分。而两者通信的纽带就是引擎上下文对象( VelocityContext实例 ),下面是常用的 VelocityContext实例 方法。
1 | 复制代码// 构造函数,入参为上下文的键值对集 |
宿主环境向模板传值
1 | 复制代码// 1. 通过构造函数传值 |
注意键值对中值的数据类型为
Integer、Long等简单数据类型的装箱类型;
String类型;
Object子类;
Object[] 数组类型,从1.6开始Velocity将数组类型视为 java.util.List 类型看待,因此模板中可调用 size() 、 get(intindex) 和 isEmpty() 的变量方法;
java.util.Collection子类;
java.util.Map子类;
java.util.Iterator对象;
java.util.Enumeration对象。
除此之外,我们还可以将一个静态类赋予到上下文对象中,如 java.lang.Math静态类
1 | 复制代码ctx.put("Math", java.lang.Math.class); |
模板向宿主环境传值
1. 通信示例1——通过引擎上下文对象获取变量
模板文件frm.vm
1 | 复制代码#set($action="./submit") |
Java代码部分
1 | 复制代码VelocityContext ctx = new VelocityContext(); |
2. 通信示例2——通过副作用修改变量、属性值
模板文件change.vm
1 | 复制代码$people.put("john", "john huang") |
Java代码部分
1 | 复制代码VelocityContext ctx = new VelocityContext(); |
上述示例表明在模板中对引用类型实例进行操作时,操作结果将影响到该引用类型实例本身,因此必须谨慎操作。
引擎上下文链
也叫容器链,目前最常用的就是提供层次数据访问和工具箱
1 | 复制代码VelocityContext context1 = new VelocityContext(); |
所谓引擎上下文链就是将原有的上下文对象赋予给新建的上下文对象,从而达到上下文内的键值对复用。具体代码如下:
1 | 复制代码VelocityContext ctx1 = new VelocityContext(); |
就是当前上下文对象没有该键值对时,则查询上下文链的对象有没有该键值对,有则返回,无则继续找链上的其他上下文对象,直到找到该键值对或遍历完所有链上的上下文对象。
但VelocityContext实例除了put、get方法外,还有remove、getKeys、containsKey方法,它们的行为又是如何的呢?下面我们通过源码来了解吧!
官网中涉及的java编码部分
自定义属性
/opt/templates
1 | 复制代码... |
虽然Velocity允许你创建自己的容器类来满足特殊的需求和技术(比如像一个直接访问LDAP服务器的容器),一个叫VelocityContext的基本实现类已经作为发行版的一部分提供给你。
VelocityContext适合所有的一般需求,所以我们强烈推荐你使用VelocityContext这个容器。只有在特殊情况和高级应用中,才需要你扩展或者创建你自己的容器实现。
for和foreach()遍历对象的支持
Velocity支持几种集合类型在VTL中使用foreach()语句:
Object []
普通对象数组 如果一个类中提供了迭代器接口,Velocity会自动包装你的数组
Velocity现在允许模板设计者把数组当作定长链表来处理(Velocity 1.6中就是这样)
java.util.Collection
Velocity通过iterator()方法返回一个迭代器在循环中使用
java.util.Map
Velocity通过接口的values()方法返回一个Collection接口,iterator()方法在它上面调用来检索用于循环的迭代器。
java.util.Iterator
目前只是暂时支持,迭代器不能重置
如果一个未初始化的迭代器被放进了容器,并且在多个foreach()语句中使用,如果第一个foreach()失败了,后面的都会被阻塞,因为迭代器不会重启
java.util.Enumeration
和java.util.Iterator一样
对于Iterator和Enumeration,推荐只有在万不得已的情况下才把它们放进容器,你也应该尽可能地让Velocity找到合适的、可复用的迭代接口。
1 | 复制代码Vector v = new Vector(); |
对静态类的支持
context.put(“Math”, Math.class);
这样你就可以在模板中用$Math引用调用java.lang.Math中的任何公有静态方法。
java.lang.Math这样的类不提供任何公有的构造函数,但是它包含了有用的静态方法
Servlet使用Velocity
web.xml 中配置Velocity
velocity.apache.org/tools/devel…
1 | 复制代码<!-- Define Velocity template handler --> |
tools.xml就像定义了一个工具箱,里面放着很多工具,比如有个“扳手”。
具体示例:考虑考虑让我们的朋友乔恩(Jon)从真实的工具箱中抓取我们的“扳手”。乔恩只需要知道我们想要哪个扳手。他不需要知道扳手做什么,也不需要知道我们打算如何做。
Velocity Toolbox的工作方式与上面的例子相同,我们仅需指定所需的工具,然后Velocity引擎就可以在vm模板中使用任何在工具箱Tool.xml中定义好的公共方法来处理其余的工作。
PipeWrench.java
1 | 复制代码public class PipeWrench { |
tools.xml
1 | 复制代码<?xml version="1.0"?> |
.vm模板中可以使用:
$wrench.size
.
VM模板
官方VTL指南:
velocity.apache.org/engine/2.2/…
VTL: Velocity Template Language
以下内容转自:www.cnblogs.com/fsjohnhuang… 肥仔John
注释
1. 行注释
1 | 复制代码## 行注释内容 |
2. 块注释
1 | 复制代码#*块注释内容1块注释内容2*# |
3. 文档注释
1 | 复制代码#**文档注释内容1文档注释内容2*# |
踩过的坑 块注释和文档注释虽然均不输出到最终结果上,但会导致最终结果出现一行空行。使用行注释则不会出现此情况。
直接输出的内容
也就是不会被引擎解析的内容。
1 | 复制代码#[[直接输出的内容1直接输出的内容2]]# |
引用
引用语句就是对引擎上下文对象中的属性进行操作
语法方面分为常规语法( $属性 )和正规语法( ${属性} )
在普通模式下上述两种写法,当引擎上下文对象中没有对应的属性时,最终结果会直接输出 $属性 或 ${属性} ,若要不输出则需要改写为 $!属性 和 $!{属性} 。
1. 变量(就是引擎上下文对象的属性)
1 | 复制代码$变量名, 常规写法,若上下文中没有对应的变量,则输入字符串"$变量名" |
变量的命名规则: 由字母、下划线(_)、破折号(-)和数字组成,而且以字母开头。
变量的数据类型为:
Integer、Long等简单数据类型的装箱类型;
String类型;
Object子类;
Object[] 数组类型,从1.6开始Velocity将数组类型视为 java.util.List 类型看待,因此模板中可调用 size() 、 get(int index) 和 isEmpty() 的变量方法;
java.util.Collection子类;
java.util.Map子类;
java.util.Iterator对象;
java.util.Enumeration对象。
2. 属性(就是引擎上下文对象的属性的属性)
1 | 复制代码$变量名.属性, 常规写法 |
属性搜索规则:
Velocity采用一种十分灵活的方式搜索变量的属性, 具体如下:
// 假如引用$var.prop,那么Velocity将对prop进行变形,然后在$var对象上尝试调用
// 变形和尝试的顺序如下
- $var.getprop()
- $var.getProp()
- $var.get(“prop”)
- $var.isProp()
// 对于$var.Prop则如下
- $var.getProp()
- $var.getprop()
- $var.get(“Prop”)
- $var.isProp()
因此获取 java.util.Map 对象的键值时可以简写为 $map.key ,Velocity会自动转为 $map.get(“key”) 来搜索!
3. 方法(就是引擎上下文对象的属性的方法)
1 | 复制代码$变量名.方法([入参1[, 入参2]*]?), 常规写法 |
引用方法实际就是方法调用操作,关注点返回值、入参和副作用的情况如下:
- 方法的返回值将输出到最终结果中
- 入参的数据类型
1 | 复制代码$变量 或 $属性,数据类型参考第一小节; |
- 副作用
1 | 复制代码// 若操作如java.util.Map.put方法,则会修改Java代码部分中的Map对象键值对 |
指令
指令主要用于定义重用模块、引入外部资源、流程控制。指令以 # 作为起始字符。
#set:向引擎上下文对象添加属性或对已有属性进行修改
格式: #set($变量 = 值) ,具体示例如下:
1 | 复制代码#set($var1 = $other) |
作用域明显是全局有效的。
#if:条件判断
格式:
1 | 复制代码#if(判断条件) |
通过示例了解判断条件:
1 | 复制代码#if($name) //$name不为false、null和undefined则视为true |
#foreach:循环
格式:
1 | 复制代码#foreach($item in $items) |
$item 的作用范围为#foreach循环体内。
$items 的数据类型为 Object[]数组 、 [1..2] 、 [1,2,3,4] 、 {a:”a”,b:”b”} 和含 public Iterator iterator() 方法的对象,具体如下:
1 | 复制代码java.util.Collection子类,Velocity会调用其iterator方法获取Iterator对象 |
内置属性$foreach.count ,用于指示当前循环的次数,从0开始。可以通过配置项 directive.foreach.maxloops 来限制最大的循环次数,默认值为-1(不限制)。
示例——使用Vector和Iterator的区别:
模板:
1 | 复制代码#macro(show) |
java代码:
1 | 复制代码Vector<String> v = new Vector<String>(); |
#break:跳出循环
1 | 复制代码#foreach($item in $items) |
#stop:中止模板解析操作
1 | 复制代码#set($cmd="stop") |
#include引入外部资源
(引入的资源不被引擎所解析)
格式: #include(resource[ otherResource]*)
resource、otherResource可以为单引号或双引号的字符串,也可以为$变量,内容为外部资源路径。注意为相对路径,则以引擎配置的文件加载器加载路径作为参考系,而不是当前模板文件的路径为参考系。
#parse引入外部资源
(引入的资源将被引擎所解析)
格式: #parse(resource)
resource可以为单引号或双引号的字符串,也可以为$变量,内容为外部资源路径。注意为相对路径,则以引擎配置的文件加载器加载路径作为参考系,而不是当前模板文件的路径为参考系。
由于#parse指令可能会引起无限递归引入的问题,因此可通过配置项 directive.parse.max.depth来限制最大递归引入次数,默认值为10.
#macro:定义重用模块(可带参数)
定义格式:
1 | 复制代码#macro(宏名 [$arg[ $arg]*]?) |
调用格式:
1 | 复制代码#宏名([$arg[ $arg]]?) |
示例1——定义与调用位于同一模板文件时,无需遵守先定义后使用的规则:
1 | 复制代码#log("What a happy day") |
示例2——定义与调用位于不同的模板文件时,需要遵守先定义后使用的规则:
1 | 复制代码## 模板文件macro.vm#macro(log $msg) |
原理解析:Velocity引擎会根据模板生成语法树并缓冲起来然后再执行,因此宏定义和调用位于同一模板文件时,调用宏的时候它已经被引擎识别并初始化了(类似js中的hosit)。
若定义与调用位于不同的模板文件中时,由于 #parse 是引擎解析模板文件时才被执行来引入外部资源并对其中的宏定义进行初始化,因此必须遵循先定义后使用的规则。
我们可配置全局宏库,配置方式如下:
1 | 复制代码Properties props = new Properties(); |
另外#macro还有另一种传参方式——$!bodyContent
1 | 复制代码#macro(say) |
#define:定义重用模块(不带参数)
1 | 复制代码#define($log) |
可视为弱版#macro,一般不用,了解就好了。
#evaluate:动态计算
示例:
1 | 复制代码#set($name = "over") |
一般不用,了解就好了。
转义符
通过 \ 对 $ 和 #进行转义,导致解析器不对其进行解析处理。
1 | 复制代码#set($test='hello') |
模板实践
内容引自:www.cnblogs.com/fsjohnhuang… 肥仔
示例结果是生成如下的html表单:
1 | 复制代码<form action="./submit"> |
引入依赖项——velocity-1.7-dep.jar
模板文件frm.vm
1 | 复制代码##表单模板 |
模板文件macro.vm
1 | 复制代码## 生成input表单元素区域的宏 |
Java代码
1 | 复制代码public static void main(String[] args) { |
参考文章
开发指南原文地址:
velocity.apache.org/engine/deve…
用户指南原文地址:
velocity.apache.org/engine/deve…
中文翻译开发指南地址:
肥仔 john优秀网文地址:
本文转载自: 掘金