Velocity
前言
由于在做比较老的PE项目,代码模板大致类似,所以想做一个代码模板,进而想到了模板引擎。thymeleaf 与 velocity 以及FreeMarker 都可以完成需求,各种调研与试用之后决定使用并学习velocity,原因有以下几种:
thymeleaf评价较差,虽然是spring boot推荐的项目
网上可以找到的性能测试结果,velocity与FreeMarker都比较强,thymeleaf 次之
thymeleaf相比velocity或者其他模板引擎的主要优势主要在于兼容xml语言,可以前端静态预览,但是相应的使用体检就变得较差,并且此功能对于我的代码生成器来说没什么作用
现有的很多的代码生成器都是使用velocity(比如人人的代码生成器),可以用来参考
FreeMarker方面,与Velocity同样常用,功能也同样强大,但是感觉可读性不如Velocity,所以再三考虑最终选择了Velocity
注意,Spring Boot 1.4以后就不支持Velocity了,对于我来说这是一个致命缺点
简介
Apache Velocity 是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。可用于生成网页、SQL、PostScript以及其他模板的输出,其它系统的集成组件等。
应用场景
Web 应用:开发者在不使用 JSP 的情况下,可以用 Velocity 让 HTML 具有动态内容的特性
源代码生成:Velocity 可以被用来生成 Java 代码、SQL 或者 PostScript等代码生成工具 自动
Email:很多软件的用户注册、密码提醒或者报表都是使用 Velocity 来自动生成的 转换 xml
相关资源
使用
添加依赖
使用步骤
初始化Velocity,单例,无需多次执行
创建上下文对象
添加变量到Context对象中
选择模板
Merge 合并模板与数据,完成输出
使用规约
不得使用``org.apache.velocity.runtime 包下的
Runtime, RuntimeConstants, RuntimeSingleton or RuntimeInstance`,这些类仅供Velocity内部使用
可以使用的是org.apache.velocity.app
下的所有类,如果此包下缺少必要的功能,建议修改
单例与非单例
Velocity.init()
方法会创建一个单例的VelocityEngine
模板引擎对象,所以,他们拥有相同的properties
配置,有时需要不同的配置,此时可以通过手动创建多例模板引擎对象来实现:
Context上下文
Context是Velocity核心,是Java层与模板层直接数据的载体。你的模板需要什么数据,只需要将其放入到其中,由模板引擎合并。
Context由VelocityContext
类定义,主要提供了两个方法:
支持#foreach
要想支持模板的#foreach循环,传入的Java对象,会依据情况做出相应处理:
数组,
java.util.List
遍历方式一致java.util.Collection
,将会使用iterator()
方法遍历java.util.Map
,依赖于接口的values()
方法来获取Collection接口,并通过iterator()
遍历java.util.Iterator
,小心使用,迭代器不可重置性,迭代器只可使用一次,意味着多个#foreach
只有第一个会成功java.util.Enumeration
, 通过枚举的hasMoreElements()
与nextElement()
方法操作,此方式与Iterator由一样的问题
放入'静态类'
有些工具类不可以被实例化,但是提供了一些公共静态方法,我们可以将其class对象放入Context中,以便使用:
Context Chaining
可以翻译为连续上下文,多个上下文之间含有链式关系,如下:
上述输出结果为 duplicate= "I am in context2"
。并且context2中包含所有context1 的变量,但是context1中的变量duplicate
仍然存在,并且值仍为I am in context1
此功能常用于分层数据访问以及工具集。
Velocity
Velocity类是一个帮助类,帮助用户轻松使用模板引擎。**Velocity运行时引擎是一个单例,同一JVM中运行所有Velocity用户资源、日志以及其他服务。**所以运行时引擎只会初始化一次,其余的尝试将会被忽略。
配置模板引擎
Velocity运行时引擎提供了五种常用方法,用于配置引擎:
setProperty( String key, Object o )
设置属性配置Object getProperty( String key )
获取属性配置init()
使用默认配置初始化引擎init( Properties p )
使用指定的Properties对象初始化引擎init( String filename )
指定配置文件路径初始化引擎
重复调用init方法无效,Velocity只会被初始化一次
渲染模板
Velocity 针对模板渲染提供了以下几个方法:
模板根路径是由
p.setProperty("resource.loader.file.path", "/opt/templates");
属性决定的
异常
在解析时,Velocity可能会抛出异常,这些异常集成RuntimeException:
ResourceNotFoundException
找不到模板资源ParseErrorException
解析时发现VTL语法错误TemplateInitException
模板初始化异常,通常是宏和指令初始化的问题MethodInvocationException
: 模板调用的方法抛出异常
日志
Velocity2.0开始使用SLF4J作为日志门面。启用日志需要:
保证slf4j的jar存在
保证slf4j具体实现的jar存在
Velocity的日志皆是以 org.apache.velocity
作为父路径的,其中包含如下几个子路径:
directive
, anddirective.
指令相关日志parser
模板解析日期loader
, andloader.
资源加载日志macro
宏相关rendering
模板渲染的相关内容,包含内省、方法调用event
事件日志
如果想要自己定义日志文件名称,可以通过runtime.log.name
属性指定
缩进控制
2.o 可以通过``parser.space_gobbling
属性控制缩进,(属性名称翻译空间吞噬???)共有
none 无空间吞噬
bc (aka backward compatible) 向后兼容
lines 默认值
structured 结构化
四种可选值。
none
不吞噬任何空间,指令所在行附近的空间,不做任何删除操作:
bc
此模式会吃掉指令后面所有的空白符:
lines
吃掉当前行的空白符:
structured
http://velocity.apache.org/engine/2.1/developer-guide.html#space-gobbling
没看明白,待解决
ResourceLoader
资源加载是Velocity的重要一部分,它负责加载模板或者是非模板资源(#include
),主要位于包org.apache.velocity.runtime.resource.loader
。Velocity目前共有四种资源加载器:
从文件获取资源,包含属性
resource.loader.file.path=rootpath1,path2,path3..
资源加载根路径resource.loader.file.cache=true/false
加载资源是否缓存resource.loader.file.modification_check_interval=100
每隔100s检查一次配置是否需要更新这是默认资源加载器
从特定的jar包中获取资源,与
很相似,只是绑定到了jar中
resource.loader.jar.path
与resource.loader.file.path
作用一致其他属性与
FileResourceLoader
一致
ClasspathResourceLoader
从类加载器中获取资源,通常是放入类路径下的模板文件,通常用于Servlet容器从URL连接获取资源:
resource.loader.url.root
url根路径resource.loader.url.cache
是否缓存resource.loader.url.modification_check_interval
检查间隔 秒resource.loader.url.timeout
超时时间 -1 代表超时时间为正无穷
DataSourceResourceLoader
从数据库中获取
ApplicationAttribute
应用程序属性是一种能够和运行时实例相关联(通过Velocity引擎或者Velocity单例)的键值对。它可以被Velocity引擎中任何运行时实例访问。这个特性是为了在应用层和Velocity引擎的特定部件之间通信的应用程序而设计的,比如日志记录器,资源加载器,资源管理器 。
举例,可以通过ApplicationAttribute 设置UrlResourceLoader的超时时间,外部在使用应用程序时,可以改变这个值,像是程序配置入口
Velocity
类中包含操作ApplicationAttribute
的相关方法:
Velocity.setApplicationAttribute( Object key, Object value )
Velocity.getApplicationAttribute( Object key )
调用时间没有要求,不一定要在init()
方法之前调用。
事件处理
Velocity含有细粒度的事件处理系统,允许自定义引擎的操作。这些事件是同步的。所有事件处理处理程序接口都位于org.apache.velocity.app.event.implement
包下,具体使用需要查看javadoc,这里只列举几例:
IncludeEventHandler
IncludeEventHandler是针对include指令的事件监听:
共有两个实现类:
IncludeNotFound (org.apache.velocity.app.event.implement)
未找到资源事件处理IncludeRelativePath (org.apache.velocity.app.event.implement)
方法参数转换
UberspectorImpl
或者SecureUberspector
会根据需要对方法参数进行类型转换:
boolean to number:
true
1, false 0boolean to string: "true" and "false"
number to boolean: 0
false
, 其他true
number to string
string to boolean: "true" 转为
true
, 其他字符串将会被转换为false(包含"false")false
string to number: 如果字符串不是数字格式,则会抛出异常
字符串下转型 : 如果不符合预期数字类型,抛出异常
string to enum constant: 如果不是预期枚举,抛出异常
string to locale (since 2.1)
下图中, implicit代表Java隐士转换,none代表无法转换,strict代表严格转换(通常指代相同类型), 而explicit代表期望转换(转换失败会抛出异常)
扩展类型转换
扩展类型转换,可以通过 Converter<T>
接口实现:
除了通过继承UberspectorImpl
类注册类型转换器,还可以
继承
org.apache.util.introspection.TypeConversionHandlerImpl
类,并在构造函数中添加转换器(2.0废弃)配置属性
runtime.conversion.handler.instance=apache.util.introspection.TypeConversionHandler
编码
Velocity允许在模板的基础上指定编码:
集成到JSR223
Velocity可以集成到Java Scripting Language Framework 即 JSR223中:
VTL详解
Velocity Template Language,是Velocity的模板语言。
velocity的模板文件以
.vm
为拓展名模板中的所有关键字都以
#
号开头,可以加入大括号#{set xx}
,#{if} #{else}
模板中的所有变量都已
$
符号开头,同样可以加入大括号 ${name}如果想要输出
$
或者#
需要在前面加上\
进行转移,相同的,如果想要输出\
则需要\\
$数字
可以直接输出,比如$1
输出为$1
解析模板
注释
单行注释
多行注释
文档注释
变量操作
velocity 中含有变量的概念,变量类型为弱类型。在执行模板时,模板可以从VelocityContext
对象中获取其中存放的值,也可以从#set($foo = "bar")
定义的变量中获取值。
变量命名规范
Velocity变量与java类似,只允许字母、下划线以及数字组成,不能以数字开头,所以$1abc
的输出结果就是$1abc
。
变量/属性输出
对应的模板为:
得到的输出结果为:
name是name是{name}的简写 二者没有什么实际区别
数组操作
除了可以通过方括号获取数组元素,Velocity引擎也提供了几个方便的方法,便于我们操作数组:
默认缺省值
在展示变量或者属性时,不可避免的出现空值,可以使用|
设置缺省值:
属性检测规则
假设有$obj.name
,Velocity模板引擎会依次从以下几个地方从obj对象中寻找此变量的值:
getname()
方法getName()
方法get("name")
方法,通常用于mapisName()
方法
如果是$obj.Name
属性,则会略有不同:
getName()
方法getname()
方法get("Name")
方法,通常用于mapisName()
方法
变量定义赋值
此外,Velocity 也支持在模板内定义变量, 可以使用set
关键字声明一个变量:
其中"velocity"
就是变量值,你可以设置:
"Hello World"
字符串字面量10
数字字面量["唐", "宋", "元"]
数组类型属性、方法引用
ArrayList、Map
方法调用
可以在VTL中调用传入的Java对象的public方法:
支持vararg methods
渲染
Velocity中的所有动态内容,比如属性、变量等,最终都会被渲染为字符串。
严格渲染模式
Strict渲染模式适用于某些特殊用途,在此模式下,任何模棱两可的表达方式,都会被模板引擎视为异常:
变量未定义或者 中不存在,引用此变量将会抛出异常
此变量未定义或者VelocityContext中不存在,或者此变量为null,引用此变量的属性会为null
#if
指令如果引用变量未定义或者为null,则返回false:#if ($foo)#end ## False
,# if(!$foo) #end ## True
指令详解
指令以 #
开头~
set
set指令用于给变量赋值或者定义变量,被赋予的值可以是:
Number literal 数字字面量
#set( $monkey.Number = 123 )
String literal 字符串字面量:
#set( $monkey.Friend = "monica" )
ArrayList
#set( $monkey.Say = ["Not", $my, "fault"] )
$monkey.Say.get(0)
Map
#set( $monkey.Map = {"banana" : "good", "roast beef" : "bad"})
$monkey.Map.banana
$monkey.Map.get("banana")
Variable reference 变量引用
#set( $monkey = $bill )
Property reference 属性引用
#set( $monkey.Blame = $whitehouse.Leak )
Method reference 方法引用
#set( $monkey.Plan = $spindoctor.weave($web) )
右侧运算
除此之外,右侧值也可以进行简单的运算:
双引号和单引号
双引号会解析引号内的变量, 此功能可以通过velocity.properties
的stringliterals.interpolate=false
属性关闭。
如果有大段代码不需要解析,可以通过如下方式设置:
输出结果:
条件判断
if/elseif/else
if的条件的判断遵循以下规则:
boolean 类型:true/false
string 类型:null与空字符被视为false
number类型: 0 视为false,其他被视为true
集合类型: 集合长度
size()
为0
逻辑运算符
逻辑与:
#if( $foo && $bar )
逻辑或:
#if( $foo || $bar )
逻辑非:
#if( !$foo )
循环
此种方式还可以对Java对象中的所有属性进行遍历,操作方式与操作map一致:
判断循环是否是第一次:
判断循环是否是最后一次:
判断循环次数是否为0:
获取当前索引(从0开始):
获取当前循环到底几次(从1开始):
设置循环次数,防止故障循环逻辑:
控制语句
break
结束当前的执行范围,也是最直接的范围,比如#foreach
#parse
,#evaluate
, #define
, #macro
或者根范围。
除此之外,break也可以跳出到指定的范围:比如,#break($macro)
, #break($foreach)
stop
停止执行模板,通常用于调试或者提示,参数可以设置提示信息: #stop('$foo 不在context中,无法继续执行')
灵活引用模板
include
include指令可以导入本地文件,并将其插入到指定的位置,本地文件的位置只能为TEMPLATE_ROOT
定义的路径下:
parse
include可以引入本地的资源,而parse可以解析本地的VTL模板:
define
为变量赋值一个VTL代码块:
velocimacros
#macro
指令允许模板设计者定义VTL模板重复段,定义与使用宏的方式如下:
宏可以包含body,使用时需要制定body,如果不指定默认为空:
宏也可以指定任意数量的参数,但是调用时必须指定相同数量的参数:
macroname是定义的宏的名称
参数类型
VTL的宏共支持如下几种参数类型:
Reference : 以$开头的任何变量
String literal : 字符串字面量
Number literal : 数字字面量
IntegerRange : 数字范围,比如
[1..2]
,[$foo .. $bar]
ObjectArray : 数组,
[ "a", "b", "c"]
boolean
配置属性
关于Velocimacro,有相关常用属性需要了解,以便灵活应用:
velocimacro.inline.allow
是否允许Velocimacro,值为true/falsevelocimacro.library.autoreload
控制Velocimacro是否自动加载,默认false,设置为true时,将会检查velocimacro 库是否发生更改,必要时进行重新加载。(前置条件:关闭缓存resource.loader.file.cache = false
),用于开发环境velocimacro.library.path
模板库路径,默认会去查找velocimacros.vtl
velocimacro.inline.replace_global
,true/false,是否覆盖全局的macro(velocimacro.library.path
中的macro),如果macro名称相同velocimacro.inline.local_scope
,true/false,如果为true,当前模板定义的micro尽可以被当前模板使用,用于限定作用域。当两个模板定义了一个相同名称的宏时,需要将
velocimacro.inline.replace_global
和velocimacro.inline.local_scope
全都设置为true
参考
最后更新于
这有帮助吗?