FreeMarker 是一个功能强大的 Java 模板引擎,广泛应用于动态内容生成、Web 页面渲染、代码生成等场景。在模板中,循环处理 是非常常见的需求,<#list> 是 FreeMarker 提供的核心循环指令,而 <#break> 则用于在循环中提前终止。本文将从 核心概念、操作步骤、常见错误、注意事项、使用技巧、最佳实践 与 性能优化 等多个维度,全面讲解 FreeMarker 中 <#list> 与 <#break> 的使用,帮助你快速掌握关键知识并实践。
一、核心概念
1. <#list> 指令
- 作用:用于遍历集合(如
List、Map、数组等)。 - 语法:
<#list collection as item> ... </#list> - 支持的集合类型:
ListMap(遍历键)- 数组
Set- 可迭代对象(如
Iterator)
2. <#break> 指令
- 作用:在
<#list>循环中提前终止循环。 - 语法:
<#if condition><#break></#if> - 注意:
<#break>只能在<#list>循环内部使用。
二、操作步骤(详细)
1. 遍历 List
<#list fruits as fruit>
<p>${fruit}</p>
</#list>
Java 示例数据:
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");
model.put("fruits", fruits);
2. 遍历 Map(键值对)
<#list user?keys as key>
<p>${key}: ${user[key]}</p>
</#list>
Java 示例数据:
Map<String, String> user = new HashMap<>();
user.put("name", "Alice");
user.put("email", "alice@example.com");
model.put("user", user);
3. 使用 <#break> 提前终止循环
<#list fruits as fruit>
<#if fruit == "Banana">
<#break>
</#if>
<p>${fruit}</p>
</#list>
说明:当
fruit等于"Banana"时,循环终止,不再输出后续元素。
4. 嵌套循环
<#list users as user>
<h3>User: ${user.name}</h3>
<ul>
<#list user.roles as role>
<li>${role}</li>
</#list>
</ul>
</#list>
Java 示例数据:
class User {
private String name;
private List<String> roles;
// getter/setter
}
List<User> users = Arrays.asList(
new User("Alice", Arrays.asList("Admin", "User")),
new User("Bob", Arrays.asList("User"))
);
model.put("users", users);
5. 使用 <#items> 和 <#no_item> 分支
<#list fruits as fruit>
<#items>
<p>${fruit}</p>
</#items>
<#no_item>
<p>No fruits available.</p>
</#no_item>
</#list>
说明:当
fruits为空或null时,执行<#no_item>分支。
三、常见错误与注意事项
1. <#break> 被误用在非 <#list> 循环中
<#if condition><#break></#if> <#-- 错误:不在循环中 -->
✅ 解决方法:确保 <#break> 仅在 <#list> 循环体内使用。
2. <#list> 传入 null 或 missing 值导致异常
<#list fruits as fruit> <#-- 如果 fruits 为 null,抛出异常 -->
✅ 解决方法:使用 ! 提供默认空集合。
<#list fruits![] as fruit>
3. <#items> 与 <#no_item> 混用不当
<#list fruits as fruit>
<#items>
...
</#items>
</#list>
⚠️ 问题:缺少 <#no_item> 时,如果集合为空不会有任何输出。
✅ 建议:根据需求决定是否添加 <#no_item> 分支。
4. 循环变量名重复导致逻辑错误
<#list users as user>
<#list user.roles as user> <#-- 错误:变量名重复 -->
✅ 解决方法:避免变量名重复,如改为 role。
四、使用技巧
1. 使用 item_index 获取当前索引(从 0 开始)
<#list fruits as fruit>
<p>No. ${fruit_item_index + 1}: ${fruit}</p>
</#list>
自动生成的变量名:
变量名 + _item_index
2. 使用 item_has_next 判断是否为最后一个元素
<#list fruits as fruit>
${fruit}<#if fruit_has_next>, </#if>
</#list>
3. 使用 <#break> 实现“取前 N 个”逻辑
<#assign count = 0>
<#list fruits as fruit>
<#if count >= 2><#break></#if>
<p>${fruit}</p>
<#assign count = count + 1>
</#list>
4. 结合 ?size 获取集合长度
Total fruits: ${fruits?size}
五、最佳实践
1. 始终使用 ![] 防止 null 异常
<#list fruits![] as fruit>
2. 避免深层嵌套循环
- 嵌套过深会导致模板可读性差。
- 建议在 Java 层预处理数据。
3. 使用 <#items> 和 <#no_item> 提高可读性
- 明确区分有数据和无数据的逻辑分支。
4. 使用 <#break> 控制循环逻辑(如取前 N 个、条件退出)
- 但避免滥用,保持逻辑清晰。
5. 使用 item_index 和 item_has_next 提升展示效果
- 用于生成序号、分隔符等。
六、性能优化
1. 避免在循环中频繁调用方法
- 方法调用会降低性能,建议在 Java 层预处理后传入模板。
2. 使用 ?size 替代手动计数
?size是内置函数,性能优于<#assign>计数。
3. 启用模板缓存
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setTemplateCacheSize(100); // 设置缓存大小
cfg.setTemplateUpdateDelay(3600); // 每小时检查一次更新
七、总结
| 技术点 | 说明 | 推荐用法 |
|---|---|---|
<#list> |
遍历集合 | <#list collection as item> |
<#break> |
提前终止循环 | <#if condition><#break></#if> |
item_index |
获取索引 | ${fruit_item_index} |
item_has_next |
判断是否为最后一个 | <#if fruit_has_next>,</#if> |
<#items> 和 <#no_item> |
控制有无数据逻辑 | <#items>...</#items><#no_item>...</#no_item> |
![] |
防止 null 异常 | <#list fruits![] as fruit> |
八、参考示例代码(Java + FTL)
Java 示例数据
Map<String, Object> model = new HashMap<>();
// List 示例
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");
model.put("fruits", fruits);
// Map 示例
Map<String, String> user = new HashMap<>();
user.put("name", "Alice");
user.put("email", "alice@example.com");
model.put("user", user);
// 嵌套结构示例
class User {
private String name;
private List<String> roles;
public User(String name, List<String> roles) {
this.name = name;
this.roles = roles;
}
public String getName() { return name; }
public List<String> getRoles() { return roles; }
}
List<User> users = Arrays.asList(
new User("Alice", Arrays.asList("Admin", "User")),
new User("Bob", Arrays.asList("User"))
);
model.put("users", users);
FTL 模板示例
<h2>List 循环</h2>
<#list fruits![] as fruit>
<#if fruit == "Banana"><#break></#if>
<p>${fruit}</p>
</#list>
<h2>Map 循环</h2>
<#list user?keys as key>
<p>${key}: ${user[key]}</p>
</#list>
<h2>嵌套循环</h2>
<#list users as user>
<h3>${user.name}</h3>
<ul>
<#list user.roles as role>
<li>${role}</li>
</#list>
</ul>
</#list>
<h2>空集合处理</h2>
<#list emptyList![] as item>
<#items>
<p>${item}</p>
</#items>
<#no_item>
<p>Empty list.</p>
</#no_item>
</#list>
九、结语
熟练掌握 FreeMarker 中 <#list> 和 <#break> 的使用,是构建高效、可维护模板系统的关键。通过合理使用循环指令、索引控制、空值处理等技巧,可以显著提升模板渲染的灵活性和健壮性。在实际开发中,建议:
- 所有集合访问都使用
![]防止null异常; - 使用
<#break>控制循环流程; - 在 Java 层预处理复杂逻辑,减少模板负担;
- 使用
<#items>和<#no_item>提高可读性; - 启用模板缓存提升性能。
✅ 最终建议:
- 避免在模板中执行复杂逻辑;
- 使用
<#break>控制循环逻辑; - 始终使用
![]防止null; - 合理使用索引和判断逻辑增强展示效果。