FreeMarker 是一个功能强大的 Java 模板引擎,广泛应用于动态内容生成、Web 页面渲染、代码生成等场景。在模板中,循环处理 是非常常见的需求,<#list>
是 FreeMarker 提供的核心循环指令,而 <#break>
则用于在循环中提前终止。本文将从 核心概念、操作步骤、常见错误、注意事项、使用技巧、最佳实践 与 性能优化 等多个维度,全面讲解 FreeMarker 中 <#list>
与 <#break>
的使用,帮助你快速掌握关键知识并实践。
一、核心概念
1. <#list>
指令
- 作用:用于遍历集合(如
List
、Map
、数组等)。 - 语法:
<#list collection as item> ... </#list>
- 支持的集合类型:
List
Map
(遍历键)- 数组
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
; - 合理使用索引和判断逻辑增强展示效果。