java通过poi-tl生成表格以及源码分析

  • 依赖
  • 模板
  • 如何动态生成表格
  • 参考文档及分析
  • 代码
 最近导出的word文件要求是越来越多了,而且对样式也做了很多要求,今天参考文档学习了一下普通表格构建表格、动态构建word表格的方法。

依赖

     <dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.10.0</version></dependency>

模板

1. {{ID}}、{{ID}} 普通文本信息占位符;
2. {{#order}} 普通word表格占位符;
3. 最后的表格是动态构建word表格的模板,样式已经设置好;

如何动态生成表格

当需求中的表格更加复杂的时候,我们完全可以设计好那些固定的部分,将需要动态渲染的部分单元格交给自定义模板渲染策略。poi-tl提供了抽象表格策略 DynamicTableRenderPolicy 来实现这样的功能。

public abstract class DynamicTableRenderPolicy implements RenderPolicy {public abstract void render(XWPFTable table, Object data);
}

{{detailTable}}标签可以在表格内的任意单元格内,DynamicTableRenderPolicy会获取XWPFTable对象进而获得操作整个表格的能力。
1、首先新建渲染策略DetailTablePolicy,继承于抽象表格策略,重写其render方法。

public class DetailTablePolicy extends DynamicTableRenderPolicy

2、然后将模板标签{{detailTable}}设置成此策略。

 Configure config = Configure.builder().bind("detailTable", new DetailTablePolicy()).build();

参考文档及分析

但是在测试是发现文档中例子始终无法生成最下面的表格,即货物明细表,
debug发现,策略类中的data为空,难怪没有生成表格。

但是数据明明已经初始化好了,那接下来就是数据在传输的是时候在哪丢失或者传进去的参数根本就没被取到。

跟踪代码,

最后在ElementProcessor中,有这样一段代码

void visit(ElementTemplate eleTemplate) {RenderPolicy policy = eleTemplate.findPolicy(template.getConfig());Objects.requireNonNull(policy, "Cannot find render policy: [" + eleTemplate.getTagName() + "]");if (policy instanceof DocxRenderPolicy) return;logger.info("Start render Template {}, Sign:{}, policy:{}", eleTemplate, eleTemplate.getSign(),ClassUtils.getShortClassName(policy.getClass()));policy.render(eleTemplate, renderDataCompute.compute(eleTemplate.getTagName()), template);}

通过policy.render(eleTemplate, renderDataCompute.compute(eleTemplate.getTagName()), template);这个方法最后进入到我们自己写的策略类DetailTablePolicy ,真是妙啊!
那既然是从这里进入我们的策略类,那也是参数的入口,就让我们看看为什么DetailTablePolicy 中data为null?

data就是renderDataCompute.compute(eleTemplate.getTagName()),其中
eleTemplate.getTagName()为detail_table而且renderDataCompute也没有与detail_table相对应的属性,虽然理论上应该是获取detailTable,但是现实是木有取到。因此也验证了我们前面的猜测,数据确实是传过来了,只是没有获取到。因此我就有个大胆的猜测,是不是因为属性字段(detail_table和detailTable)没有完全对应,因此我就把detail_table替换成了detailTable(代码和docx模板全部替换),结果妹想到呀,成了!!!
哈哈哈,真是走了狗屎运了。还望路过的各位大佬指点指点

补充renderDataCompute.compute(eleTemplate.getTagName()):
虽然走了狗屎运,但是还是想探个究竟,compute到底做了些什么
进入DefaultELRenderDataCompute类中

public Object compute(String el) {try {if (null != envObject && !el.contains("#this")) {try {Object val = envObject.eval(el);if (null != val) {return val;}} catch (Exception e) {// ignore}}// 进入这个方法return elObject.eval(el);} catch (ExpressionEvalException e) {if (isStrict) throw e;// Cannot calculate the expression, the default returns nullreturn null;}}

进入DefaultEL类中

public Object eval(String el) {// THIS是什么? private static final String THIS = "#this";if (THIS.equals(el)) {return model;}Dot dot = new Dot(el);// 继续进入return dot.eval(this);}

在Dot类中终于找到了!!!

public Object eval(DefaultEL elObject) {// 如果cache中有el相等的key,则返回cache中相应的valueif (elObject.cache.containsKey(el)) return elObject.cache.get(el);// 如果target为null result就为evalKey(elObject.model)// evalKey   是啥???  真是要了老命了,我以为结束了呢,继续看方法evalKey吧Object result = null != target ? result = evalKey(target.eval(elObject)) : evalKey(elObject.model);if (null != result) elObject.cache.put(el, result);return result;}private Object evalKey(Object obj) {if (null == obj) {throw new ExpressionEvalException("Error eval " + key + ", the value of " + target + " is null");}final Class<?> objClass = obj.getClass();if (obj instanceof String || obj instanceof Number || obj instanceof java.util.Date || obj instanceof Collection|| obj instanceof Boolean || objClass.isArray() || objClass.isPrimitive()) {throw new ExpressionEvalException("Error eval " + key + ", the type of " + target + "must be Hash, but is " + objClass);}// 重点是这里  如果是Map类型  返回对应的value// 但是我们的elObject.model是Map吗? 看下图if (obj instanceof Map) {return ((Map<?, ?>) obj).get(key);}// introspectorMethod readMethod = ReadMethodFinder.find(objClass, key);if (null != readMethod) {try {return readMethod.invoke(obj);} catch (Exception e) {logger.info("Introspector {} fail: {}", key, e.getMessage());}}// 通过对象的类信息反射返回对应属性的值// reflectField field = FieldFinder.find(objClass, key);if (null == field) {throw new ExpressionEvalException("Cannot find property " + key + " from " + objClass);} else {try {return field.get(obj);} catch (Exception e) {throw new ExpressionEvalException("Error read the property:" + key + " from " + objClass);}}}

elObject.model和elObject.cache的类型

static Field find(Class<?> objClass, String key) {Class<?> clazz = objClass;Field field = null;while (clazz != Object.class) {try {field = findInClazz(clazz, key);// 暴力访问field.setAccessible(true);return field;} catch (NoSuchFieldException e) {// do nothing, go to super class} catch (Exception e) {logger.warn("Error read the property:" + key + " from " + objClass);}clazz = clazz.getSuperclass();}return null;}static Field findInClazz(Class<?> clazz, String key) throws NoSuchFieldException {Field field = null;try {// 通过类信息获取对应字段的值field = clazz.getDeclaredField(key);return field;} catch (Exception e) {}Field[] fields = cache.get(clazz);if (null == fields) {fields = clazz.getDeclaredFields();cache.put(clazz, fields);}for (Field f : fields) {Name annotation = f.getAnnotation(Name.class);if (null != annotation && key.equals(annotation.value())) {return f;}}throw new NoSuchFieldException(key);}

Poi-tl Documentation

附上代码如下:

代码

  • 实体类DetailData(动态构建word表格的数据)
public class DetailData {private List<RowRenderData> goods;private  List<RowRenderData> labors;public List<RowRenderData> getGoods() {return goods;}public void setGoods(List<RowRenderData> goods) {this.goods = goods;}public List<RowRenderData> getLabors() {return labors;}public void setLabors(List<RowRenderData> labors) {this.labors = labors;}
}
  • 实体类PaymentData (相当于数据集,包换word模板中需要的数据都在这里)
public class PaymentData {private  String NO;private  String ID;private  String taitou;private  String consignee;private  String subtotal;private  String tax;private  String transform;private  String other;private  String unpay;private  String total;private TableRenderData order;private DetailData detailTable;public String getNO() {return NO;}public void setNO(String NO) {this.NO = NO;}public String getID() {return ID;}public void setID(String ID) {this.ID = ID;}public String getTaitou() {return taitou;}public void setTaitou(String taitou) {this.taitou = taitou;}public String getConsignee() {return consignee;}public void setConsignee(String consignee) {this.consignee = consignee;}public String getSubtotal() {return subtotal;}public void setSubtotal(String subtotal) {this.subtotal = subtotal;}public String getTax() {return tax;}public void setTax(String tax) {this.tax = tax;}public String getTransform() {return transform;}public void setTransform(String transform) {this.transform = transform;}public String getOther() {return other;}public void setOther(String other) {this.other = other;}public String getUnpay() {return unpay;}public void setUnpay(String unpay) {this.unpay = unpay;}public String getTotal() {return total;}public void setTotal(String total) {this.total = total;}public TableRenderData getOrder() {return order;}public void setOrder(TableRenderData order) {this.order = order;}public DetailData getDetailTable() {return detailTable;}public void setDetailTable(DetailData detailTable) {this.detailTable = detailTable;}
}
  • 策略类(动态构建表格的策略方法)
public class DetailTablePolicy extends DynamicTableRenderPolicy {// 货品填充数据所在行数int goodsStartRow = 2;// 人工费填充数据所在行数int laborsStartRow = 5;@Overridepublic void render(XWPFTable table, Object data) throws Exception {if (null == data) return;DetailData detailData = (DetailData) data;// 人工费List<RowRenderData> labors = detailData.getLabors();if (null != labors) {table.removeRow(laborsStartRow);// 循环插入行for (int i = 0; i < labors.size(); i++) {XWPFTableRow insertNewTableRow = table.insertNewTableRow(laborsStartRow);for (int j = 0; j < 7; j++) insertNewTableRow.createCell();// 合并单元格TableTools.mergeCellsHorizonal(table, laborsStartRow, 0, 3);// 单行渲染TableRenderPolicy.Helper.renderRow(table.getRow(laborsStartRow), labors.get(i));}}// 货物List<RowRenderData> goods = detailData.getGoods();if (null != goods) {table.removeRow(goodsStartRow);for (int i = 0; i < goods.size(); i++) {XWPFTableRow insertNewTableRow = table.insertNewTableRow(goodsStartRow);for (int j = 0; j < 7; j++) insertNewTableRow.createCell();TableRenderPolicy.Helper.renderRow(table.getRow(goodsStartRow), goods.get(i));}}}
@DisplayName("Example for Table")
public class generateTable{String resource = "src/main/resources/payment.docx";PaymentData datas = new PaymentData();// 初始化数据@BeforeEachpublic  void init() {datas.setNO("KB.6890451");datas.setID("ZHANG_SAN_091");datas.setTaitou("深圳XX家装有限公司");datas.setConsignee("丙丁");datas.setSubtotal("8000");datas.setTax("600");datas.setTransform("120");datas.setOther("250");datas.setUnpay("6600");datas.setTotal("总共:7200");RowRenderData header = Rows.of("日期", "订单编号", "销售代表", "离岸价", "发货方式", "条款", "税号").bgColor("F2F2F2").center().textColor("7F7f7F").textFontFamily("Hei").textFontSize(9).create();RowRenderData row = Rows.of("2018-06-12", "SN18090", "李四", "5000元", "快递", "附录A", "T11090").center().create();BorderStyle borderStyle = new BorderStyle();borderStyle.setColor("A6A6A6");borderStyle.setSize(4);borderStyle.setType(XWPFTable.XWPFBorderType.SINGLE);TableRenderData table = Tables.ofA4MediumWidth().addRow(header).addRow(row).border(borderStyle).center().create();datas.setOrder(table);DetailData detailTable = new DetailData();RowRenderData good = Rows.of("4", "墙纸", "书房+卧室", "1500", "/", "400", "1600").center().create();List<RowRenderData> goods = Arrays.asList(good, good, good);RowRenderData labor = Rows.of("油漆工", "2", "200", "400").center().create();List<RowRenderData> labors = Arrays.asList(labor, labor, labor, labor);detailTable.setGoods(goods);detailTable.setLabors(labors);datas.setDetailTable(detailTable);}// 测试方法@Testpublic void testPaymentExample() throws Exception {Configure config = Configure.builder().bind("detailTable", new DetailTablePolicy()).build();XWPFTemplate template = XWPFTemplate.compile(resource, config).render(datas);template.writeToFile("out_example_payment.docx");}}

java通过poi-tl模板引擎生成表格(Word)相关推荐

  1. Java使用POI通过模板生成Word

    Java使用POI通过模板生成Word 前言 最近工作需要用到,所以记录下来以便查找. 一.概述 POI读写word使用的核心类是XWPFDocument.一个XWPFDocument代表一个docx ...

  2. Java中利用freemarker模板动态生成word含表格

    最近公司有导出word的需求,由于word的样式有的很复杂所以记录一下Java中利用freemarker模板动态生成word含表格,以防以后忘记. 1.word表格的模板 删掉无用的数据留下基础的样式 ...

  3. Beetl 模板引擎生成word以及excel总结

    Beetl Java模板引擎生成word excel 之前项目中使用freemarker和POI进行word以及excel的模板导出,在使用的过程中为了解决一些小问题,意外的接触了Beetl这款模板生 ...

  4. POI导入模板,下载表格接口

    POI导入模板,下载表格接口 package com.lg.ticket.utils;import cn.hutool.poi.excel.RowUtil; import com.baomidou.m ...

  5. JAVA 利用poi EXCLE模板文档导出数据

    JAVA 利用poi EXCLE模板文档导出数据 1.导入jar包 下载地址:添加链接描述 提取码:xqkg 2.EXCLE模板 3.代码示例 package utill;import java.io ...

  6. java freemarker 模版_Java模板引擎-FreeMarker

    简介: FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写.FreeMarker我们的第一印象是用来替代JSP的,但是与JSP不同的是FreeMarker模板可 ...

  7. Java之利用Freemarker模板引擎实现代码生成器,提高效率

    开心一笑 [1.你以为我会眼睁睁的看着你去送死?我会闭着眼睛.2.给你讲个故事,从前有个笨蛋,他非常笨,别人问他问题他只会回答"没有",这个故事你听过吗?] 视频教程 大家好,我录 ...

  8. POI之PPT中生成表格简单实例

    开心一笑 昨晚被一道神题考住了! ( )( ) ( )2 4 6 7 8 让我填空-我按照数列组合算了一下午都不对 最后, 答案是这样的 (门前大桥下)(游过一群鸭) (快来快来数一数) 2 4 6 ...

  9. 工具集核心教程 | 第五篇: 利用Velocity模板引擎生成模板代码

    前言 不知道大家有没有这样的感觉,在平时开发中,经常有很多dao.service类中存着很多重复的代码,Velocity提供了模板生成工具,今天我教大家怎么和这些大量的重复代码说再见. 参考项目:ht ...

最新文章

  1. 优化网站设计方案提升网站用户回头率
  2. vue-cli 脚手架搭建
  3. JS_鼠标移入移出渐变效果(类似css3中的transition或animation和@keyframes)
  4. mysql 查看锁_MySQL反应慢的排查思路
  5. 【英语学习】【Level 08】U01 Let's Read L2 Of fairies and princesses
  6. 写给科技公司项目经理的一封警告书
  7. 2017.5.20欢(bei)乐(ju)赛解题报告
  8. mysql update多表联合更新
  9. Benchmark与Profiler---性能调优得力助手
  10. 网页上的在线打印如何下载成本地PDF格式(人工亲测)
  11. python联合vrep_python控制vrep代码实例
  12. html颜色渐变配色方案,WebGradients – 提供180种渐变配色灵感的网站
  13. ps滑动鼠标放大缩小
  14. 基于cnn的人脸识别_人脸识别技术全面总结:从传统方法到深度学习
  15. CSS 排版与正常流 —— 重学CSS
  16. JavaScript 实现BASE58加密 中文英文数字都可以加密
  17. 友价实现一键Nofollow(数据库替换)
  18. 如何用python解方程组_python如何解方程组
  19. 浅谈filter中的chain.doFilter(request, response)的作用
  20. Python 3 Keras YOLO v3解析与实现

热门文章

  1. Java面试题汇总大全(重点)
  2. FinalData专业级数据恢复系统
  3. 学习C语言的反思与总结
  4. BuildR Procedural Building Generator使用教程-建筑建模
  5. CDISC学习之SDTMIG(3.2版本)
  6. EasyScheduler学习(一):部署与安装
  7. 如何使用 UML 工具开发 EA 交易
  8. 对AnyChat录屏解决方案的调研报告
  9. C语言:自定义字符串函数
  10. svga文件预览_Android动画SVGA的使用