接手一个用Velocity渲染邮件模板的老项目,改了一行#foreach,整封邮件正文变成空白,日志里干干净净什么都没记。Velocity的坑大多不在语法本身,而在它跟JSP的思维差异,以及出错时习惯性静默失败。
跟JSP最不一样的地方
JSP最终编译成Servlet,能直接写Java代码,逻辑可以很重。Velocity只是模板引擎,靠$变量和#指令做替换,复杂逻辑必须放进Java的context里。好处是模板干净,前端能自己改;坏处是很多人拿它当JSP用,在模板里拼命写#if嵌套,最后没人看得懂。
#foreach的几个坑
#foreach($item in $list) $foreach.index $item.name #end$foreach.index从0开始,$foreach.count从1开始,这两个特别容易混。判断是不是最后一个用$foreach.hasNext。最常见的坑是集合为null时,整段#foreach不输出,页面上少一块内容却没有任何报错。稳妥写法是先用#if($list && !$list.isEmpty())包一层,或者直接写$!list。另外#foreach里对集合做增删,会抛ConcurrentModificationException,要改就先用临时集合。
变量渲染的静默失败
$name在name不存在时,原样输出$name这五个字符,用户直接看到美元符号。$!name则输出空。这个差别调试时要命:页面显示$userName,说明context里根本没有这个key,不是值算错了。取对象属性失败也一样,$user.name原样吐回来,通常是getter对不上。
宏定义的两条规矩
#macro定义可复用片段,参数按引用传,宏里#set的值会带出去,这点跟函数返回值完全不同,容易在循环里互相污染。#macro必须定义在使用之前,写在文件末尾会报错,工程里通常单独放一个vm文件,用#parse引入。宏名和变量名别重名,重名时宏优先,排查起来非常费劲。
缓存和调试
生产环境一定要开resource.loader.cache=true,否则每次请求都重新读盘解析模板,QPS一高磁盘先扛不住。开了之后改模板看不到效果是正常的,要清缓存或者重启。调试时把log指到单独文件,level调成debug,异常会写到那里,而不是混在应用日志里。
- 排错顺序:先确认context有没有值,再看模板语法,最后才怀疑引擎版本。
- 空白页面:九成是#end少了,或者#if条件里的变量为null。