| 指令 | 語法 | 說明 |
|---|---|---|
#foreach | #foreach($item in $collection) ... #end | 遍歷集合、Map、數組、Iterator、Enumeration |
#if / #else / #elseif | #if($condition) ... #else ... #end | 條件判斷 |
#set | #set($var = $value) | 賦值變量 |
$!{var} | $!{nullableVar} | 靜默引用:值為 null 時輸出空字符串而非 $nullableVar |
public class VelocityController {
public void renderTemplate() {
VelocityContext context = new VelocityContext();
// 1. Map 簡單鍵值對
Map paramValues = new LinkedHashMap<>(); // 保持插入順序
paramValues.put("username", "zhangsan");
paramValues.put("email", "zhangsan@example.com");
paramValues.put("role", "admin");
context.put("paramValues", paramValues);
// 2. List 對象集合
List userList = userService.findAll(); // 假設 User 有 name、age、email 屬性
context.put("userList", userList);
// 3. Map 復雜結構
Map dashboard = new HashMap<>();
dashboard.put("totalUsers", 1250);
dashboard.put("onlineCount", 89);
dashboard.put("recentLogs", logService.getRecent(10));
context.put("dashboard", dashboard);
// 渲染模板
StringWriter writer = new StringWriter();
velocityEngine.mergeTemplate("template.vm", "UTF-8", context, writer);
// 輸出 writer.toString() 到響應/文件/郵件正文
}
}
<h3>用戶基本信息</h3>
<table class="table table-bordered">
<thead>
<tr><th>屬性</th><th>值</th></tr>
</thead>
<tbody>
#foreach($key in $paramValues.keySet())
<tr>
<td>$key</td>
<td>$!{paramValues.get($key)}</td> <!-- $!{} 防 null 輸出 $... -->
</tr>
#end
</tbody>
</table>
<h3>儀表盤數據</h3>
<ul>
#foreach($entry in $dashboard.entrySet())
<li><strong>$entry.key</strong>:
#if($entry.value instanceof List)
<ul>
#foreach($item in $entry.value)
<li>$item</li>
#end
</ul>
#else
$!{entry.value}
#end
</li>
#end
</ul>
<h3>用戶列表</h3>
<table class="table table-striped">
<thead>
<tr><th>姓名</th><th>年齡</th><th>郵箱</th><th>操作</th></tr>
</thead>
<tbody>
#foreach($user in $userList)
<tr>
<td>$!{user.name}</td>
<td>$!{user.age}</td>
<td>$!{user.email}</td>
<td>
<a href="/user/edit/$!{user.id}">編輯</a> |
<a href="/user/delete/$!{user.id}" onclick="return confirm('確定刪除?')">刪除</a>
</td>
</tr>
#end
</tbody>
</table>
<tbody>
#foreach($user in $userList)
#set($rowClass = $velocityCount % 2 == 0 ? "even" : "odd")
<tr class="$rowClass">
<td>$velocityCount</td> <!-- 從 1 開始的計數器 -->
<td>$!{user.name}</td>
<td>$!{user.email}</td>
<td>
#if($velocityHasNext) <span class="has-next">↓</span> #end
</td>
</tr>
#end
</tbody>
內置變量:$velocityCount (1-based 計數)、$velocityHasNext (是否有下一項)、$velocityIndex (0-based 索引)。
#foreach($row in $reportData)
<tr>
#foreach($cell in $row.entrySet())
<td>$!{cell.value}</td>
#end
</tr>
#end
| 問題 | 原因 | 解決 |
|---|---|---|
模板中輸出 $paramValues.get($key) 字面量 | Velocity 不支持直接調用帶參數方法 | 後台轉為 List 或模板用 #set($val = $paramValues.get($key)) $val |
| Map 遍歷順序混亂 | HashMap 無序 | 後台用 LinkedHashMap、TreeMap 或模板端排序 |
null 顯示為 $user.name | 引用為 null 時默認輸出引用字面量 | 用 $!{user.name} 靜默引用 |
| 集合為空時不輸出提示 | 無判斷邏輯 | #if($userList.size() > 0) ... #else 暫無數據 #end |
| 模板編碼亂碼 | 模板文件編碼與 mergeTemplate 指定編碼不一致 | 統一 UTF-8,IDEA/Eclipse 設置文件編碼 |
$!{}:防止 null 洩露到頁面。#macro(row $user) ... #end,#row($user) 調用。runtime.references.strict = true (Velocity 2.x),未定義引用直接拋異常,提前發現拼寫錯誤。resource.loader = file + file.resource.loader.cache = true。Velocity 遍歷集合核心是 #foreach($item in $collection),配合 $velocityCount、$!{}、#if 等內置變量/指令,可實現複雜的列表、表格、巢狀結構渲染。關鍵在於後台準備有序、扁平、無 null 惡意的數據結構,模板層純粹做展示組裝。掌握 Map 遍歷 keySet()/entrySet()、List 遍歷、靜默引用、索引變量四大核心模式,覆蓋 95% 的模板開發場景。