1. 概述
poi-tl 是基于 Apache POI 的开源 Java 库,核心功能是简化基于模板的 Word 文档生成。它作为 Word 模板引擎,通过 Word 模板和数据生成新文档。
我们可以在模板中预定义样式,生成文档时自动保留这些样式。模板采用声明式设计,完全基于标签实现,不同标签对应图片、文本、表格等不同元素。poi-tl 还支持自定义插件,实现灵活的文档结构定制。
本文将深入探讨模板中的各类标签用法及自定义插件开发。
2. 依赖配置
使用 poi-tl 需添加 Maven 依赖:
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.12.2</version>
</dependency>
最新版本可在 Maven Central 查询。
3. 核心配置
通过 ConfigureBuilder
构建配置对象:
ConfigureBuilder builder = Configure.builder();
...
XWPFTemplate template = XWPFTemplate.compile(...).render(templateData);
template.writeAndClose(...);
模板文件需为 Word 的 .docx
格式。核心流程分为三步:
- 使用
XWPFTemplate.compile()
编译模板 - 通过
render()
将templateData
渲染到新文档 - 调用
writeAndClose()
生成文件并关闭资源
其中 templateData
是 HashMap<String, Object>
实例。以下是关键配置项:
3.1. 标签分隔符
默认使用 {{}}
作为标签标记,可自定义其他形式:
builder.buildGramer("${", "}");
3.2. 标签类型标识符
默认使用特定符号标识标签类型(如 @
表示图片,#
表示表格),支持自定义:
builder.addPlugin('@', new TableRenderPolicy());
builder.addPlugin('#', new PictureRenderPolicy());
3.3. 标签命名规则
标签名默认支持字母、数字、下划线组合,可通过正则表达式自定义规则:
builder.buildGrammerRegex("[\\w]+(\\.[\\w]+)*");
后续章节将介绍插件和错误处理等高级配置。本文剩余部分均采用默认配置。
4. 模板标签详解
poi-tl 模板不包含变量赋值或循环控制,完全依赖标签驱动。数据通过 Map
或 DataModel
与标签关联。标签基本语法:
{{tagName}}
下面分类说明常用标签:
4.1. 文本标签
普通文本使用双花括号包裹标签名:
{{authorname}}
在 template.docx
中添加此标签后,通过 Map
设置数据:
this.templateData.put("authorname", Texts.of("John")
.color("000000")
.bold()
.create());
渲染时将替换为 "John" 并应用指定的样式(黑色加粗)。
4.2. 图片标签
图片标签以 @
开头:
{{@companylogo}}
在数据映射中指定图片路径:
templateData.put("companylogo", "logo.png");
4.3. 编号列表标签
编号列表标签以 *
开头:
{{*bulletlist}}
通过 NumberingBuilder
构建列表数据:
List<String> list = new ArrayList<String>();
list.add("Plug-in grammar");
// ...
NumberingBuilder builder = Numberings.of(NumberingFormat.DECIMAL);
for(String s:list) {
builder.addItem(s);
}
NumberingRenderData renderData = builder.create();
this.templateData.put("list", renderData);
支持多种编号格式:
NumberingFormat.DECIMAL
(十进制数字)NumberingFormat.LOWER_LETTER
(小写字母)NumberingFormat.LOWER_ROMAN
(小写罗马数字)
4.4. 区块标签
区块由开始/结束标签对构成,开始标签带 ?
前缀,结束标签带 /
前缀:
{{?students}} {{name}} {{/students}}
添加区块数据:
Map<String, Object> students = new LinkedHashMap<String, Object>();
students.put("name", "John");
students.put("name", "Mary");
students.put("name", "Disarray");
this.templateData.put("students", students);
4.5. 表格标签
表格标签以 #
开头:
{{#table0}}
使用 Tables
类构建表格数据:
templateData.put("table0", Tables.of(new String[][] {
new String[] { "00", "01" },
new String[] { "10", "11" }
})
.border(BorderStyle.DEFAULT)
.create());
高级用法:
- 通过
Rows.of()
单独定义行样式:RowRenderData row01 = Rows.of("Col0", "col1", "col2") .center() .bgColor("4472C4") .create(); RowRenderData row11 = Rows.of("Col10", "col11", "col12") .center() .bgColor("4472C4") .create(); templateData.put("table3", Tables.of(row01, row11) .create());
- 使用
MergeCellRule
合并单元格:MergeCellRule rule = MergeCellRule.builder() .map(Grid.of(1, 0), Grid.of(1, 2)) .build(); templateData.put("table3", Tables.of(row01, row11) .mergeRule(rule) .create());
4.6. 嵌套模板
嵌套模板标签以 +
开头:
{{+nested}}
设置嵌套模板数据:
List<Address> subData = new ArrayList<>();
subData.add(new Address("Florida,USA"));
subData.add(new Address("Texas,USA"));
templateData.put("nested", Includes.ofStream(
WordDocumentEditor.class.getClassLoader()
.getResourceAsStream("nested.docx"))
.setRenderModel(subData)
.create());
引擎会将 nested.docx
渲染结果作为标签值插入主文档。
4.7. 使用 DataModel 渲染
支持通过 DataModel
绑定数据。创建 Person
类:
public class Person {
private String name;
private int age;
// ...
}
绑定数据模型:
templateData.put("person", new Person("Jimmy", 35));
模板中使用 .
运算符访问属性:
{{person.name}}
{{person.age}}
5. 插件系统
插件允许在标签位置执行预定义逻辑,实现复杂操作。poi-tl 提供三类插件:
- 默认插件(无需配置)
- 内置插件(需配置)
- 自定义插件
5.1. 使用内置插件
以 Word 注释插件为例,通过 CommentRenderPolicy
配置:
builder.bind("comment", new CommentRenderPolicy());
使用插件:
CommentRenderData comment = Comments.of("SampleExample")
.signature("John", "S", LocaleUtil.getLocaleCalendar())
.comment("Authored by John")
.create();
templateData.put("comment", comment);
其他可用插件见官方文档。
5.2. 自定义插件开发
需实现 RenderPolicy
接口或继承 AbstractRenderPolicy
:
public class SampleRenderPolicy implements RenderPolicy {
@Override
public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
XWPFRun run = ((RunTemplate) eleTemplate).getRun();
String text = "Sample plugin " + String.valueOf(data);
run.setText(text, 0);
}
}
注册自定义插件:
ConfigureBuilder builder = Configure.builder();
builder.bind("sample", new SampleRenderPolicy());
templateData.put("sample", "custom-plugin");
渲染结果:将 {{sample}}
替换为 "Sample plugin custom-plugin"。
6. 日志配置
通过 Logback 启用日志,在 logback.xml
添加:
<logger name="com.deepoove.poi" level="debug" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
典型日志输出:
18:01:15.488 [main] INFO c.d.poi.resolver.TemplateResolver - Resolve the document start...
18:01:15.503 [main] DEBUG c.d.poi.resolver.RunningRunBody - {{title}}
18:01:15.503 [main] DEBUG c.d.poi.resolver.RunningRunBody - [Start]:The run position of {{title}} is 0, Offset in run is 0
...
18:01:19.685 [main] INFO c.deepoove.poi.render.DefaultRender - Successfully Render template in 4126 millis
通过日志可追踪标签解析和数据渲染过程。
7. 错误处理机制
poi-tl 支持自定义错误处理策略:
常见错误场景:
- 引用不存在的变量
- 级联属性为 null(如
{{student.name}}
中student
为 null)
处理策略配置:
- 默认行为:返回 null(清除标签)
builder.useDefaultEL(true);
- 静默忽略标签:
builder.setValidErrorHandler(new DiscardHandler());
- 严格模式(抛异常):
builder.setValidErrorHandler(new AbortHandler());
8. 动态模板生成
模板引擎不仅能生成文档,还能创建新模板。示例:将文本标签拆分为文本+表格标签:
Configure config = Configure.builder()
.bind("title", new DocumentRenderPolicy())
.build();
Map<String, Object> data = new HashMap<>();
DocumentRenderData document = Documents.of()
.addParagraph(Paragraphs.of("{{title}}").create())
.addParagraph(Paragraphs.of("{{#table}}").create())
.create();
data.put("title", document);
引擎会将 title
标签替换为文档结构模板。
9. 总结
本文系统讲解了 poi-tl 的核心功能: ✅ 模板标签系统(文本/图片/表格/列表等) ✅ 插件开发机制(内置+自定义) ✅ 日志与错误处理配置 ✅ 动态模板生成技巧
⚠️ 实际开发中建议优先采用模板标签实现基础功能,复杂场景再结合插件扩展。遇到样式问题可检查 Word 模板中的样式定义是否正确。