一、前言                            

Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力。而且Velocity被移植到不同的平台上,如.Net的NVelocity和js的Velocity.js,虽然各平台在使用和实现上略有差别,但大部分语法和引擎核心的实现是一致的,因此学习成本降低不少哦。

最好的学习资源——官网:http://velocity.apache.org/

本系列打算采用如下结构对Velocity进行较为全面的学习,若有不妥或欠缺望大家提出,谢谢。

  1. 入门示例

  2. VTL语法详解

 3. 模板与宿主环境通信

  4. 基础配置项

  5. 深入模板引擎及调优配置

二、VTL语法详解                        

VTL的语句分为4大类:注释直接输出的内容引用指令。另外由于VTL中以 # 和 $ 作为关键字起始字符,因此输出它们时需要通过转义符 \ 来将其转换为普通字符。

由于内容较多,特设目录一坨!

  三. 注释行注释块注释文档注释

 四. 直接输出的内容

五. 引用变量属性方法

 六. 指令#set#if#foreach#include#parse#break#stop#macro#define#evaluate

  七、转义符

三、注释                            

  1. 行注释

## 行注释内容

  2. 块注释

#*
块注释内容1
块注释内容2
*#

  3. 文档注释

#**
文档注释内容1
文档注释内容2
*#

  踩过的坑 

块注释和文档注释虽然均不输出到最终结果上,但会导致最终结果出现一行空行。使用行注释则不会出现此情况。

四、直接输出的内容                            

也就是不会被引擎解析的内容。

#[[
直接输出的内容1
直接输出的内容2
]]#

五、引用                                   

引用语句就是对引擎上下文对象中的属性进行操作。语法方面分为常规语法( $属性 )和正规语法( ${属性} )。在普通模式下上述两种写法,当引擎上下文对象中没有对应的属性时,最终结果会直接输出 $属性 或 ${属性} ,若要不输出则需要改写为 $!属性 和 $!{属性} 。

   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. 属性(就是引擎上下文对象的属性的属性)

$变量名.属性, 常规写法
${变量名.属性}, 正规写法
$!变量名.属性, 常规写法
$!{变量名.属性}, 正规写法

属性搜索规则:

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[, 入参2]*]?), 常规写法
${变量名.方法([入参1[, 入参2]*]?)}, 正规写法
$!变量名.方法([入参1[, 入参2]*]?), 常规写法
$!{变量名.方法([入参1[, 入参2]*]?)}, 正规写法

引用方法实际就是方法调用操作,关注点返回值入参副作用的情况如下:

1. 方法的返回值将输出到最终结果中

2. 入参的数据类型

$变量 或 $属性,数据类型参考第一小节;
范围操作符(如:[..]或[$arg1..$arg2]),将作为java.util.ArrayList处理
字典字面量(如:{a:"a",b:"b"}),将作为java.util.Map处理
数字字面量(如:),将自动装箱或拆箱匹配方法定义中的int或Integer入参

3. 副作用

// 若操作如java.util.Map.put方法,则会修改Java代码部分中的Map对象键值对
$map.put("key", "new value")

六、指令                              

指令主要用于定义重用模块、引入外部资源、流程控制。指令以 # 作为起始字符。

  1. #set:向引擎上下文对象添加属性或对已有属性进行修改

格式: #set($变量 = 值) ,具体示例如下:

#set($var1 = $other)
#set($var1.prop1 = $other)
#set($var = )
#set($var = true)
#set($var = [,])
#set($var = {a:"a", b:"b"})
#set($var = [..])
#set($var = [$arg1..$arg2])
#set($var = $var1.method())
#set($var = $arg1 + )
#set($var = "hello")
#set($var = "hello $var1") // 双引号可实现字符串拼接(coffeescript也是这样哦!),假设$var1为fsjohnhuang,则$var为hello fsjohnhuang
#set($var = 'hello $var1') // 单引号将不解析其中引用,假设$var1为fsjohnhuang,则$var为hello $var1

作用域明显是全局有效的。

  2. #if:条件判断

格式:

#if(判断条件)
.........
#elseif(判断条件)
.........
#else
.........
#end

通过示例了解判断条件:

#if($name)   //$name不为false、null和undefined则视为true
$name
#elseif($job == "net") // ==和!=两边的变量将调用其toString(),并对比两者的返回值
Net工程师
#elseif($age <= ) // 支持<=,>=,>,<逻辑运算符
未成年劳工
#elseif(!$married) // 支持!逻辑运算符
未婚
#elseif($age >= && !$married) // 支持&&,||关系运算符
大龄未婚青年
#end

  3. #foreach:循环

格式:

#foreach($item in $items)
..........
#end

$item 的作用范围为#foreach循环体内。

$items 的数据类型为 Object[]数组 、 [..] 、 [,,,] 、 {a:"a",b:"b"} 和含 public Iterator iterator() 方法的对象,具体如下:

java.util.Collection子类,Velocity会调用其iterator方法获取Iterator对象
java.util.Map子类,Velocity会调用value()获取Collection对象,然后调用调用其iterator方法获取Iterator对象
java.util.Iterator对象,直接将该Iterator对象添加到上下文对象中时,由于Iterator对象为只进不退的操作方式,因此无法被多个#foreach指令遍历
java.util.Enumeration对象,直接将该Enumeration对象添加到上下文对象中时,由于Iterator对象为只进不退的操作方式,因此无法被多个#foreach指令遍历

内置属性$foreach.count ,用于指示当前循环的次数,从0开始。可以通过配置项 directive.foreach.maxloops 来限制最大的循环次数,默认值为-1(不限制)。

示例——使用Vector和Iterator的区别:

模板:

#macro(show)
#foreach($letter in $letters)
$letter
#end
#end
#show()
#show(

Java代码部分:

Vector<String> v = new Vector<String>();
v.add("a");
v.add("b");
VelocityContext ctx = new VelocityContext();
ctx.put("letters",v);
Template t = Velocity.getTemplate("模板路径");
StringWriter sw = new StringWriter();
t.merge(ctx,sw);
System.out.println(sw.toString());
// 结果
// a
// b
// a
// b ctx.put("letters",v.iterator());
// 结果
// a
//

  4. #break:跳出循环

#foreach($item in $items)
#if($item == "over")
#break;
#end
$item
#end

  5. #stop:中止模板解析操作

#set($cmd="stop")
$cmd
#if($cmd == "stop")
#stop
#end
$cmd // 该语句将不执行

  6. #include:引入外部资源(引入的资源不被引擎所解析)

格式: #include(resource[ otherResource]*)

resource、otherResource可以为单引号或双引号的字符串,也可以为$变量,内容为外部资源路径。注意为相对路径,则以引擎配置的文件加载器加载路径作为参考系,而不是当前模板文件的路径为参考系。

  7. #parse:引入外部资源(引入的资源将被引擎所解析)

格式: #parse(resource)

resource可以为单引号或双引号的字符串,也可以为$变量,内容为外部资源路径。注意为相对路径,则以引擎配置的文件加载器加载路径作为参考系,而不是当前模板文件的路径为参考系。

由于#parse指令可能会引起无限递归引入的问题,因此可通过配置项 directive.parse.max.depth来限制最大递归引入次数,默认值为10.

  8. #macro:定义重用模块(可带参数)

定义格式:

#macro(宏名 [$arg[ $arg]*]?)
.....
#end

调用格式:

#宏名([$arg[ $arg]]?)

示例1——定义与调用位于同一模板文件时,无需遵守先定义后使用的规则

#log("What a happy day")
#macro(log $msg)
log message: $msg
#end

示例2——定义与调用位于不同的模板文件时,需要遵守先定义后使用的规则

## 模板文件macro.vm
#macro(log $msg)
log message: $msg
#end
## 模板文件main.vm
#parse("macro.vm")
#log("What a happy day")

原理解析:Velocity引擎会根据模板生成语法树并缓冲起来然后再执行,因此宏定义和调用位于同一模板文件时,调用宏的时候它已经被引擎识别并初始化了(类似js中的hosit)。

若定义与调用位于不同的模板文件中时,由于 #parse 是引擎解析模板文件时才被执行来引入外部资源并对其中的宏定义进行初始化,因此必须遵循先定义后使用的规则。

我们可配置全局宏库,配置方式如下:

Properties props = new Properties();
// velocimacro.library的值为模板文件的路径,多个路径时用逗号分隔
// velocimacro.library的默认值为VM_global_library.vm
props.setProperty("velocimacro.library", "global_macro1.vm,global_macro2.vm");
VelocityEngine ve = new VelocityEngine(props);

另外#macro还有另一种传参方式——$!bodyContent

#macro(say)
$!bodyContent
#end
#@say()Hello World#end
// 结果为Hello World

  9. #define:定义重用模块(不带参数)

示例:

#define($log)
hello log!
#end
$log

可视为弱版#macro,一般不用,了解就好了。

  10. #evaluate:动态计算

示例:

#set($name = "over")
#evalute("#if($name=='over')over#{else}not over#end") // 输出over

一般不用,了解就好了。

七、转义符                            

通过 \ 对 $ 和 #进行转义,导致解析器不对其进行解析处理。

#set($test='hello')
$test ## 结果:hello
\$test ## 结果:$test
\\$test ## 结果:\hello
\\\$test ## 结果:\$test $!test ## 结果:hello
$\!test ## 结果:$!test
$\\!test ## 结果:$\!test
$\\\!test ## 结果:$\\!test

八、总结                             

VTL语法部分KO了,接下来就是模板与宿主环境通信——核心在引擎上下文对象(VelocityContext)上!

尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4112866.html ^_^肥仔John

Velocity魔法堂系列二:VTL语法详解的更多相关文章

  1. Velocity魔法堂系列一:入门示例(转)

    Velocity魔法堂系列一:入门示例 一.前言 Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本 ...

  2. Velocity魔法堂系列三:模板与宿主环境通信

    一.前言 Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力.而且Velocity被移植到不 ...

  3. Velocity魔法堂系列一:入门示例

    一.前言 Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力.而且Velocity被移植到不 ...

  4. Azure Terraform(二)语法详解

    一,引言 上篇文章开始,我们简单介绍了以下通过基础设施管理工具----- Terraform,通过它来统一管理复杂的云基础设施资源.作为入门演示,使用Terraform 部署Azure 资源组的方式直 ...

  5. elasticsearch系列二:索引详解(快速入门、索引管理、映射详解、索引别名)

    一.快速入门 1. 查看集群的健康状况 http://localhost:9200/_cat http://localhost:9200/_cat/health?v 说明:v是用来要求在结果中返回表头 ...

  6. WPF系列 Path表示语法详解(Path之Data属性语法)

    示例: XAML(代码A): <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ...

  7. 深入理解JAVA I/O系列二:字节流详解

    流的概念 JAVA程序通过流来完成输入/输出.流是生产或消费信息的抽象,流通过JAVA的输入输出与物理设备链接,尽管与它们链接的物理设备不尽相同,所有流的行为具有相同的方式.这样就意味一个输入流能够抽 ...

  8. Java 8系列之Stream的基本语法详解

    本文转至:https://blog.csdn.net/io_field/article/details/54971761 Stream系列: Java 8系列之Stream的基本语法详解 Java 8 ...

  9. ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借

    ASP.NET MVC深入浅出系列(持续更新)   一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...

随机推荐

  1. 努力学习 HTML5 (2)—— 元素的增和删

    HTML5 放松了某些规则,HTML5 的制定者想让这门语言更紧密地反映浏览器的现实. 放松的规则 不要求包含 <html>.<head> 和 <body> 元素. ...

  2. AngularJS指令嵌套时link函数执行顺序的问题

    今天研究指令嵌套时,发现子指令的link函数先于父指令的link函数执行. 这样和预想的顺序不一样. 也就是说,如果子指令的某个scope变量依赖于父指令传来的参数时,可能一直是undefinded比 ...

  3. http 请求类

    1.httpclient请求类 代理demo:http://hc.apache.org/httpcomponents-client-4.3.x/httpclient/examples/org/apac ...

  4. ORA-00257: archiver error. Connect internal only, until freed——解决

    参考http://www.2cto.com/database/201109/104615.html, 开启归档后,操作一个大表迁移表空间,执行了1个多小时没完成就手动给中断了,但是再次用plsql登陆 ...

  5. GitHub 优秀的 Android 开源项目(转)

    今天查找资源时看到的一篇文章,总结了很多实用资源,十分感谢原作者分享. 转自:http://blog.csdn.net/shulianghan/article/details/18046021 主要介 ...

  6. Linux 升级 Python 至 3.x

    https://docs.python.org/3.5/library/json.html 在线手册 (1)下载wget https://www.python.org/ftp/python/3.5.2 ...

  7. [Javascript] The "this" keyword

    The very first thing to understand when we're talking about this-keyword is really understand what's ...

  8. 【过程改进】总结大中小型项目的git流程

    git作为源码管理工具出于流行趋势.这里和大家一起分享下我们是如何用git的分支(branch)功能管理不同规模的项目 小型项目 推荐工具:TortoiseGit 开发阶段(第一版上线前):2个分支 ...

  9. Java中RSA非对称密钥加解密使用示例

    一.简介: RSA加密算法是最常用的非对称加密算法,CFCA在证书服务中离不了它.RSA是第一个比较完善的公开密钥算法,它既能用于加密,也能用于数字签名.这个算法经受住了多年深入的密码分析,虽然密码分 ...

  10. bit操作 转

    http://www.catonmat.net/blog/low-level-bit-hacks-you-absolutely-must-know/ Bit Hack #6. Turn off the ...