基于Java反射和Excel模版实现高效Excel导出功能

前言

在实际开发中,总免不了需要做一些数据的导出功能,也有很多第三方的POI可以使用,也提供了很多有用的api可以使用,例如:设置单元格样式等。虽然提供的api很齐全,但要去设置好一个样式等也是需要大量的代码和精力,同时最重要的是也加大了代码出现异常的风险。
所以提前做好一个Excel模版,在需要导出的时候,只需要往Excel中的单元格填充数据即可,这样也暴露一个问题,就是需要手动去创建单元格,并且手动去对应每个单元格下的值,同时要保证所在的单元格位置和对应的值是正确的。
那么如何让模版中的单元格下的值能够自动一一对应呢?正好,反射就可以解决这个问题,这样减少了代码量,同时在模版变动的时候也只需要花费较少的时间去维护。

实战

在这里插入图片描述
说明:
需要导出的模版,这里只是简单写一个,张三和18就是需要我们在实际开发中从数据库查询出来填充到模版中的数据。

public class ExcelWrapper {
    /**名称*/
    private String name;
    /**年龄*/
    private Integer age;
}

说明:
这里注意的是ExcelWrapper中的属性必须和Excel中的表头顺序是一致的,否则会造成数据对应错误。

1.把List的数据填充到Excel模版中

public Workbook getSheets(List list) throws IOException {
        // excel模板路径
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("excel/demo.xlsx");
        Workbook wb = new XSSFWorkbook(inputStream);
        // 读取了模板内所有sheet内容
        Sheet sheet = wb.getSheetAt(0);
        // 在相应的单元格进行赋值(模版默认从第二行开始赋值)
        int rowIndex = 1;
        for (ExcelWrapper wrapper : list) {
            Map fieldValueMap = ExcelUtil.getFieldValueMap(wrapper);
            Set keySet = fieldValueMap.keySet();
            for (int i = 0; i < keySet.size() - 1; i++) {
                setCellValue(sheet, rowIndex, i, fieldValueMap.get(i));
            }
            rowIndex++;
        }
        return wb;
    }

2.生成单元格并赋值

public void setCellValue(Sheet sheet, int rowIndex, Integer i, Object o) {
        Row row = sheet.getRow(rowIndex);
        if (null == row) {
            row = sheet.createRow(rowIndex);
        }
        Cell cell = row.getCell(i);
        if (null == cell) {
            cell = row.createCell(i);
        }
        cell.setCellValue(String.valueOf(o));
    }

3.封装的工具类

// 用序号作为key,属性值作为value
 public static Map getFieldValueMap(Object obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        Map map = new HashMap<>(16);
        for (int i = 0; i < fields.length; i++) {
            map.put(i, getFieldValue(fields[i].getName(), obj));
        }
        return map;
    }

说明:
此处用序号作为key可以在给指定的单元格赋上对应的值。即:需要填充的值“张三”对应的位置就是018对应的位置是1,以此类推。。。这就是序号最大的好处。

// 获取到对象属性值
public static Object getFieldValue(String field, Object obj) {
      try {
            PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field, obj.getClass());
            Method readMethod = propertyDescriptor.getReadMethod();
            return readMethod.invoke(obj, null);
        } catch (Exception e) {
            log.info("getFieldValue Exception Cause by {}", e);
        }
        return  null;
    }

说明
PropertyDescriptor是Java自带属性描述器,使用getReadMethod就不需要我们自己去拼接getXxx这样的方式去获取值,用起来非常方便。

简单总结

优点:

  1. 使用模版导出,避免了在代码中设置样式,减少代码量,减少出错的几率;
  2. 使用反射的方式将实体本身无规律的属性映射到Map中,转换成key-value形式,并且将序号作为key,这样就不需要关心每一个单元格需要填充哪个属性值,减少了出错的几率,同时也减少了代码量。
  3. 代码复用性比较好,更换了模版一样能使用,只需要更改较少的代码,甚至完全不需要该代码。

缺点

  1. 模版中的单元格位置必须和填充数据的实体属性顺序一一对应,一旦改了实体位置,那么将会使模版填充的数据无法对应上而导致数据错误。
  2. 中间套用了三个循环,或多或少会影响一些性能(或许还可以优化)。

你可能感兴趣的:(功能,导出,Excel)