Velocity是Apache软件基金会旗下的一款Java模板引擎,它的核心作用是把数据和模板拼装成最终文本。模板中可以使用$变量输出内容,用#if、#foreach等指令控制逻辑,Java程序侧则通过VelocityContext把数据传给模板。对于需要频繁生成重复性文本的场景,比如动态页面、代码生成器、邮件模板、SQL脚本生成,Velocity能避免大量字符串拼接,让模板维护更清晰。与JSP不同,Velocity不依赖Servlet容器,可以脱离Web环境独立使用,这也是它在工具类项目中受欢迎的原因之一。

接下来从基础语法、配置方式、操作要点和常见疑问几个方面展开。先抓住最常用的几个指令,能够跑通一个完整示例,后续再逐步深入。
一、认识Velocity的核心语法
Velocity模板本质是一个文本文件,可以包含普通文字、变量引用和指令。变量引用最常见的形式是$name,也可以写成${name}。当引擎渲染到$name时,会从传入的上下文中查找name对应的值并替换;如果找不到,默认会原样输出$name,而不是报错。如果希望变量不存在时输出空字符串,可以使用静默引用形式$!name,或者$!{name}。
在属性访问上,Velocity支持点号访问,例如$user.name,它会先尝试调用getUser(),再尝试get("name"),最后尝试获取字段值。方法调用也很直接,$user.getName()可以输出Java对象的方法返回值。但需要注意,Velocity在模板中调用方法时,参数传递和返回值处理遵循Java反射规则,如果方法抛出异常,渲染会中断,因此要谨慎调用可能抛异常的方法。
指令方面,#set用于定义临时变量,比如#set($total = $price * $count)。#if配合#elseif和#else做条件分支,#foreach用于遍历集合,格式为#foreach($item in $list) ... #end。还有#include和#parse用于引入其他文件,区别在于#include只做纯文本包含,#parse会当作Velocity模板解析。这些指令组合起来,可以处理大部分日常模板需求。
二、Java侧配置与模板加载
在Java代码中使用Velocity,通常需要初始化VelocityEngine,并设置相关属性。一个常见的最小化配置如下:先创建VelocityEngine对象,设置resource.loader为file或classpath,再调用init()方法完成初始化。模板文件可以放在文件系统目录,也可以放在classpath下,具体取决于resource.loader配置。对于Web应用,还可以使用WebappResourceLoader从应用根目录加载模板。
渲染时,需要创建VelocityContext对象,把数据以键值对形式放进去,再通过getTemplate()方法获取模板,最后用StringWriter接收输出。示例代码思路:VelocityContext context = new VelocityContext(); context.put("name", "张三"); Template template = engine.getTemplate("hello.vm"); StringWriter writer = new StringWriter(); template.merge(context, writer); 最后writer.toString()就是渲染结果。这里的编码设置非常关键,如果模板文件包含中文,建议在engine属性中设置input.encoding和output.encoding为UTF-8,避免乱码。
缓存方面,Velocity默认会缓存模板,修改模板文件后可能不会立即生效。开发阶段可以把file.resource.loader.cache设置为false,或者调整modificationCheckInterval属性,让引擎更频繁地检查模板变更。生产环境保留缓存可以提升性能,但要注意热更新需求。
三、常见疑问与避坑要点
第一个常见问题是变量不显示或原样输出$name。这通常是因为变量名拼写不一致,或者变量值为null。使用$!name能让null值输出空白,但排查时最好先确认context中确实放入了该key。另外,Velocity对null的处理比较宽容,但在#if判断里,null会被当作false处理,这点与Java不同。
第二个问题是#if判断不符合预期。比如#set($flag = "false")后,直接用#if($flag)判断,很多开发者以为字符串"false"会被当成假,实际上在Velocity中非空字符串会被当作true,只有null和Boolean false才是false。要判断字符串内容,应写成#if($flag == "false")或者#if($flag != "true")。类似地,比较数字时要注意Velocity的等号判断会区分类型,建议使用#set($num = 0)后通过#if($num == 0)判断,而不是依赖于隐式转换。
第三个是中文乱码。乱码往往不是模板引擎本身的问题,而是字符集配置不一致。需要确保模板文件保存为UTF-8,Java读取时使用UTF-8,输出到页面或文件时也使用UTF-8。在Velocity属性中同时设置input.encoding=UTF-8和output.encoding=UTF-8,可以覆盖大部分场景。如果仍然乱码,检查请求和响应编码是否也保持一致。
第四个是安全问题。如果模板内容允许用户输入,可能存在模板注入风险。Velocity模板可以调用Java对象方法,恶意用户可能通过反射访问系统类,造成信息泄露或命令执行。因此,永远不要将用户输入直接作为模板内容解析。如果业务上必须支持动态模板,应该限制可用变量和方法,或使用沙箱机制,并做好权限隔离。
最后,foreach遍历时空指针也值得注意。如果传入的list为null,Velocity的#foreach循环不会执行,但也不会报错;如果list本身是空集合,循环体同样会被跳过。可以使用#if($list && $list.size() > 0)包裹,再执行遍历,让逻辑更清晰。另外,$foreach.count和$foreach.index分别表示从1开始和从0开始的计数,容易混淆,使用时注意区分。
四、日常操作要点与最佳实践
在项目中使用Velocity时,建议把模板文件按业务模块划分目录,不要全部堆在根目录。模板扩展名通常使用.vm,但也可以自定义。命名上尽量使用有意义的名称,例如order_confirm.vm、email_welcome.vm,便于维护。
对于重复出现的片段,可以使用#parse引入公共文件,比如页头、页脚、公共宏。宏可以用#macro定义,但注意宏的参数传递和调用方式。Velocity的宏不支持递归调用,避免在宏内部再次调用自身。如果多个模板需要共享变量,建议在渲染前统一准备数据,不要把复杂逻辑写在模板里,保持模板职责单一。
性能方面,Velocity的模板缓存能显著降低重复解析开销,但要注意修改模板后的加载策略。频繁变更的模板可以关闭缓存或缩短检查间隔。对于较大的集合,foreach遍历本身不会成为主要瓶颈,真正影响性能的是循环体内的字符串拼接和方法调用,尽量在Java侧预先处理好数据,再交给模板输出。
另外,日志和异常处理也值得关注。Velocity在解析模板语法错误时会抛出ParseException,在渲染时可能抛出ResourceNotFoundException或MethodInvocationException。捕获这些异常并记录模板名称、行号等信息,可以大幅提高排错效率。建议在开发阶段开启模板修改检查,配合IDE插件或文本编辑器的高亮支持,减少语法错误。
Velocity模板引擎Apache Velocity模板渲染修改时间:2026-09-19 01:18:05