合同文档生成是企业级开发中的高频需求。想象这样一个场景:销售部门每天需要处理上百份格式固定但客户信息、产品清单各不相同的合同,手动修改不仅效率低下还容易出错。这正是poi-tl这类模板引擎大显身手的时刻——通过预先设计好的Word模板配合动态数据绑定,实现批量自动化文档生成。
在pom.xml中添加以下依赖时需特别注意版本兼容性:
xml复制<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.10.3</version>
</dependency>
<!-- 建议配套使用的POI版本 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.2</version>
</dependency>
提示:实际项目中若遇到
NoClassDefFoundError,通常是由于POI子模块版本不一致导致,建议使用mvn dependency:tree检查依赖冲突。
在Word模板中设计占位标签时,推荐使用以下规范:
{{companyName}}(普通文本){{@logo}}(带@前缀){{#products}},行尾设置{{/products}}
图示:黄色高亮部分为模板标签,实际生成时会被替换为动态数据
数据准备阶段需要将业务对象转换为模板引擎能识别的结构:
java复制Map<String, Object> data = new HashMap<>();
// 基础文本
data.put("contractNo", "HT20230001");
data.put("signDate", LocalDate.now().format(DateTimeFormatter.ISO_DATE));
// 动态表格数据
List<Map<String, Object>> products = new ArrayList<>();
order.getItems().forEach(item -> {
Map<String, Object> row = new HashMap<>();
row.put("name", item.getName());
row.put("spec", item.getSpecification());
row.put("price", item.getPrice());
products.add(row);
});
data.put("products", products);
// 公司Logo
data.put("logo", new PictureRenderData(100, 50,
Files.newInputStream(Paths.get("logo.png"))));
对于合同常见的金额大写、日期中文格式等需求,推荐在数据准备阶段完成转换:
java复制// 金额转中文大写
data.put("totalAmountCN", MoneyUtils.toChinese(amount));
// 日期转中文格式
String signDateCN = DateFormatUtils.format(date, "yyyy年MM月dd日");
data.put("signDateCN", signDateCN);
针对不同类型的模板元素需要配置对应的渲染策略:
java复制Configure config = Configure.builder()
// 绑定表格循环策略
.bind("products", new LoopRowTableRenderPolicy())
// 自定义金额格式化策略
.bind("amount", (el, data, template) -> {
DecimalFormat df = new DecimalFormat("#,##0.00");
el.text(df.format(data));
})
.build();
生成最终文档时建议添加异常处理和资源清理:
java复制try (InputStream templateStream = getClass()
.getResourceAsStream("/templates/contract.docx");
OutputStream out = new FileOutputStream("output.docx")) {
XWPFTemplate template = XWPFTemplate
.compile(templateStream, config)
.render(data);
template.write(out);
} catch (Exception e) {
log.error("合同生成失败", e);
throw new RuntimeException("文档生成异常");
}
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 标签未替换 | 标签名称不匹配 | 检查模板与代码中的key是否完全一致 |
| 图片显示异常 | 图片路径错误 | 使用绝对路径或确保资源在classpath中 |
| 表格循环失效 | 未绑定渲染策略 | 检查Configure是否正确配置 |
XWPFTemplate实例ParallelStream处理大批量文档生成java复制// 模板缓存示例
private static final Map<String, XWPFTemplate> templateCache = new ConcurrentHashMap<>();
public XWPFTemplate getTemplate(String path) throws IOException {
return templateCache.computeIfAbsent(path, p -> {
try {
return XWPFTemplate.compile(getClass().getResourceAsStream(p));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
poi-tl同样适用于以下场景:
生成Word后常需转换为PDF,推荐组合使用:
java复制// 使用pdfbox转换
PDDocument pdfDoc = Loader.loadPDF(new File("output.docx"));
pdfDoc.save("contract.pdf");
实际项目中更推荐使用专业转换工具如Aspose或OpenPDF以获得更好的格式保持能力。