一、前言
Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力。而且Velocity被移植到不同的平台上,如.Net的NVelocity和js的Velocity.js,虽然各平台在使用和实现上略有差别,但大部分语法和引擎核心的实现是一致的,因此学习成本降低不少哦。
最好的学习资源——官网:http://velocity.apache.org/
本系列打算采用如下结构对Velocity进行较为全面的学习,若有不妥或欠缺望大家提出,谢谢。
2. VTL语法详解
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语法详解的更多相关文章
-
Velocity魔法堂系列一:入门示例(转)
Velocity魔法堂系列一:入门示例 一.前言 Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本 ...
-
Velocity魔法堂系列三:模板与宿主环境通信
一.前言 Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力.而且Velocity被移植到不 ...
-
Velocity魔法堂系列一:入门示例
一.前言 Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力.而且Velocity被移植到不 ...
-
Azure Terraform(二)语法详解
一,引言 上篇文章开始,我们简单介绍了以下通过基础设施管理工具----- Terraform,通过它来统一管理复杂的云基础设施资源.作为入门演示,使用Terraform 部署Azure 资源组的方式直 ...
-
elasticsearch系列二:索引详解(快速入门、索引管理、映射详解、索引别名)
一.快速入门 1. 查看集群的健康状况 http://localhost:9200/_cat http://localhost:9200/_cat/health?v 说明:v是用来要求在结果中返回表头 ...
-
WPF系列 Path表示语法详解(Path之Data属性语法)
示例: XAML(代码A): <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ...
-
深入理解JAVA I/O系列二:字节流详解
流的概念 JAVA程序通过流来完成输入/输出.流是生产或消费信息的抽象,流通过JAVA的输入输出与物理设备链接,尽管与它们链接的物理设备不尽相同,所有流的行为具有相同的方式.这样就意味一个输入流能够抽 ...
-
Java 8系列之Stream的基本语法详解
本文转至:https://blog.csdn.net/io_field/article/details/54971761 Stream系列: Java 8系列之Stream的基本语法详解 Java 8 ...
-
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的无状态模 ...
随机推荐
-
开源服务专题之------ssh防止暴力破解及fail2ban的使用方法
15年出现的JAVA反序列化漏洞,另一个是redis配置不当导致机器入侵.只要redis是用root启动的并且未授权的话,就可以通过set方式直接写入一个authorized_keys到系统的/roo ...
-
Collection框架
Collection框架 集合框架(Collection Framework)泛指java.util包的若干个类和接口.如Collection,List,ArrayList,LinkedList,Ve ...
-
11g新特性-查询缓存(1)
众所周知,访问内存比访问硬盘快得多,除非硬盘体系发生革命性的改变.可以说缓存在Oracle里面无处不在,结果集缓存(Result Cache)是Oracle Database 11g新引入的功能,引入 ...
-
leetcode371. Sum of Two Integers
Calculate the sum of two integers a and b, but you are not allowed to use the operator + and -. Exam ...
-
Javascript笔记一
Javascript: ECMAscript :相当于翻译器 翻译电脑于代码 解释器 DOM document object model 文档 对象 模型 --->document 获取 ...
-
远程连接Ucenter数据库
网站和Ucenter不是同一服务器的连接方法~我折腾了好几天,终于找到了这方法!各位连接不上的不妨试试~什么事只有试过才知道行不行! define('UC_CONNECT', 'mysql'); de ...
-
java学习(五)--- 方法
方法的定义 修饰符 返回值类型 方法名(参数类型 参数名){ ... 方法体 ... return 返回值; } 注意:非void方法必须有返回值 方法重载: 可以声明方法相同,但是参数类型不同的方法 ...
-
Golang微服务:micro实践
micro 使用 工具安装 使用protoc生成代码,依赖两个插件:protoc-gen-go.protoc-gen-micro micro 工具 go get -u github.com/micro ...
-
Go语言无锁队列组件的实现 (chan/interface/select)
1. 背景 go代码中要实现异步很简单,go funcName(). 但是进程需要控制协程数量在合理范围内,对应大批量任务可以使用"协程池 + 无锁队列"实现. 2. golang ...
-
UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe0 in position 0
Windows 7/8/10机器上安装Python 2.7后,下载一些Package包进行setup时总是报错UnicodeDecodeError,如下: File "C:/Python27 ...