2. 概述
本文将聚焦 Mustache 模板引擎,并使用其 Java API 生成动态 HTML 内容。
Mustache 是一个无逻辑模板引擎,用于创建 HTML、配置文件等动态内容。
3. 简介
简单来说,该引擎被归类为"无逻辑",因为它不支持 if-else 语句或 for 循环等控制结构。
Mustache 模板由被 {{ }}
包围的标签名组成(因其形似胡子而得名),并由包含模板数据的模型对象支持。
4. Maven 依赖
Mustache 模板的编译和执行支持多种语言(客户端和服务器端)。要在 Java 中处理模板,我们使用其 Java 库,可通过 Maven 依赖添加:
Java 8+:
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.9.4</version>
</dependency>
Java 6/7:
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.8.18</version>
</dependency>
⚠️ 最新版本可在 Maven 中央仓库 查询。
5. 使用
我们通过一个简单场景演示:
- 编写简单模板
- 使用 Java API 编译模板
- 提供必要数据执行模板
5.1 一个简单的 Mustache 模板
创建一个显示待办任务详情的模板:
<h2>{{title}}</h2>
<small>创建于 {{createdOn}}</small>
<p>{{text}}</p>
花括号 {{}}
内的字段可以是:
- Java 类的方法或属性
Map
对象的键
5.2 编译 Mustache 模板
编译模板的代码如下:
MustacheFactory mf = new DefaultMustacheFactory();
Mustache m = mf.compile("todo.mustache");
MustacheFactory
会在类路径中查找模板。本例中,我们将 todo.mustache
放在 src/main/resources
下。
5.3 执行 Mustache 模板
提供给模板的数据是 Todo
类的实例:
public class Todo {
private String title;
private String text;
private boolean done;
private Date createdOn;
private Date completedOn;
// 构造函数、getter 和 setter
}
执行编译后的模板生成 HTML:
Todo todo = new Todo("Todo 1", "Description");
StringWriter writer = new StringWriter();
m.execute(writer, todo).flush();
String html = writer.toString();
6. Mustache 区块与迭代
现在看看如何列出待办任务。迭代列表数据时,我们使用 Mustache 区块。
区块是根据当前上下文中键的值重复渲染的代码块,格式如下:
{{#todo}}
<!-- 其他代码 -->
{{/todo}}
区块以 #
开始,以 /
结束,符号后跟随用于渲染区块的键。根据键值的不同,可能出现以下场景:
6.1 非空列表或非假值的区块
创建使用区块的模板 todo-section.mustache
:
{{#todo}}
<h2>{{title}}</h2>
<small>创建于 {{createdOn}}</small>
<p>{{text}}</p>
{{/todo}}
测试该模板:
@Test
public void givenTodoObject_whenGetHtml_thenSuccess()
throws IOException {
Todo todo = new Todo("Todo 1", "Todo description");
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todo.mustache");
Map<String, Object> context = new HashMap<>();
context.put("todo", todo);
String expected = "<h2>Todo 1</h2>";
assertThat(executeTemplate(m, todo)).contains(expected);
}
创建列出多个待办任务的模板 todos.mustache
:
{{#todos}}
<h2>{{title}}</h2>
{{/todos}}
使用该模板生成列表:
@Test
public void givenTodoList_whenGetHtml_thenSuccess()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todos.mustache");
List<Todo> todos = Arrays.asList(
new Todo("Todo 1", "Todo description"),
new Todo("Todo 2", "Todo description another"),
new Todo("Todo 3", "Todo description another")
);
Map<String, Object> context = new HashMap<>();
context.put("todos", todos);
assertThat(executeTemplate(m, context))
.contains("<h2>Todo 1</h2>")
.contains("<h2>Todo 2</h2>")
.contains("<h2>Todo 3</h2>");
}
6.2 空列表、假值或空值的区块
用 null
值测试 todo-section.mustache
:
@Test
public void givenNullTodoObject_whenGetHtml_thenEmptyHtml()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todo-section.mustache");
Map<String, Object> context = new HashMap<>();
assertThat(executeTemplate(m, context)).isEmpty();
}
用空列表测试 todos.mustache
:
@Test
public void givenEmptyList_whenGetHtml_thenEmptyHtml()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todos.mustache");
Map<String, Object> context = new HashMap<>();
assertThat(executeTemplate(m, context)).isEmpty();;
}
7. 反转区块
反转区块仅在键不存在、值为 false/null 或空列表时渲染一次。简单说,当区块不渲染时,反转区块就会渲染。
反转区块以 ^
开始,以 /
结束:
{{#todos}}
<h2>{{title}}</h2>
{{/todos}}
{{^todos}}
<p>没有待办任务!</p>
{{/todos}}
当提供空列表时测试该模板:
@Test
public void givenEmptyList_whenGetHtmlUsingInvertedSection_thenHtml()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todos-inverted-section.mustache");
Map<String, Object> context = new HashMap<>();
assertThat(executeTemplate(m, context).trim())
.isEqualTo("<p>没有待办任务!</p>");
}
8. Lambda 表达式
Mustache 区块的键值可以是函数或 Lambda 表达式。此时,完整的 Lambda 表达式会被调用,并将区块内的文本作为参数传入。
看模板 todos-lambda.mustache
:
{{#todos}}
<h2>{{title}}{{#handleDone}}{{doneSince}}{{/handleDone}}</h2>
{{/todos}}
handleDone
键对应 Java 8 Lambda 表达式:
public Function<Object, Object> handleDone() {
return (obj) -> done ?
String.format("<small>完成于 %s 分钟前</small>", obj) : "";
}
执行上述模板生成的 HTML:
<h2>Todo 1</h2>
<h2>Todo 2</h2>
<h2>Todo 3<small>完成于 5 分钟前</small></h2>
9. 总结
本文介绍了 Mustache 模板的基础用法,包括区块、反转区块和 Lambda 表达式,并通过 Java API 演示了如何编译和执行模板。
Mustache 还有更多高级特性值得探索:
- ✅ 提供 Callable 作为值实现并发求值
- ✅ 使用
DecoratedCollection
获取集合元素的 first/last/index 信息 - ✅
invert
API(根据文本和模板反推数据)
完整源代码可在 GitHub 获取。