目录
对EasyExcel的调研
在经历了EasyPoi的坑后决定后面放弃使用这个工具(垃圾玩意对不起GVP徽章)。偶然间看到了阿里开源的EasyExcel工具,在看了其api和实现思路后发现是一个很好的工具,随后调研了特性发现并不支持自动合并含有合并单元格的数据,需要自行在ReadListener中处理。如果每一个需求都要自行处理一遍那么效率实在是有点低下,随后便开始研究EasyExcel的处理原理。
github地址:https://github.com/zhaoqiang1024/easyexcel
分支:
- dev-1.0基于3.0.5版本开发
- dev-2.0基于3.1.1版本开发
几个关键的类
- ExcelHeadProperty
该类是用于解析Excel的Head配置,主要有字段所对应的列索引、行的Class类型、需要忽略的字段、标题头的起行号。该类还有最重要的功能,通过Class对象来解析出对的字段Field并转换成Head对象
- Head
存储了一个字段的配置,例如:所在列索引、字段的Field对象、字段的FieldName、该字段的自定义格式等
- ModelBuildEventListener
一个实现了ReadListener接口的对象接收转换类,该类将提供了将原始数据转换成指定的Object对象功能;
- DefaultAnalysisEventProcessor
一个默认的解析处理器,用于解析Head和Excel的数据,循环调用ReadListener监听器等;
理清原生的处理逻辑后决定将ModelBuildEventListener替换掉,开发一个可支持合并的监听器。
主要的思路有几点:
- 尽量不要破坏原生的逻辑减少侵入
尽大可能保证原生代码,以便于后续的升级
- 扩展Head对象
1、扩展type类型字段。用于存储该字段的类型:普通orList
2、扩展nextHead Map字段。当该Head的type类型为List时,nextHead字段存储了List范型所解析出来的Head集合
3、扩展collectionClass字段。用于存储List集合所对应的范型类型
- 扩展注解
扩展@ExcelCollection注解。仅用于标注该字段为一个List类型,需要进行合并
- 自定义监听器AbstractReadMergeListener
该监听器的大致思想为2次数据读取:
1、将解析后的Map<Integer, ReadCellData<?>> 数据和Map<Integer,CellExtra>数据在内存中进行存储。
private TreeMap<Integer, Map<Integer, ReadCellData<?>>> dataMaps = new TreeMap<>();
private TreeMap<Integer, Map<Integer,CellExtra>> extraMap =new TreeMap<>();
@Override
public void invoke(Map<Integer, ReadCellData<?>> cellDataMap, AnalysisContext context) {
dataMaps.put(context.readSheetHolder().getRowIndex(),cellDataMap);
}
@Override
public void extra(CellExtra extra, AnalysisContext context) {
Integer rowHeadNumber = context.readSheetHolder().getHeadRowNumber();
if (extra.getRowIndex() <= rowHeadNumber-1) {
return;
}
Map<Integer, CellExtra> cellMap = new TreeMap<>();
if(extraMap.containsKey(extra.getRowIndex())){
cellMap = extraMap.get(extra.getRowIndex());
}
cellMap.put(extra.getFirstColumnIndex(), extra);
extraMap.put(extra.getRowIndex(), cellMap);
}
2、待Excel解析完毕后在doAfterAllAnalysed方法中处理单元格合并问题,通过行号与extraMap中存储的合并信息进行匹配,如果匹配到相应的合并规则后进行解析,按照规则所对应的行进行合并,主要采用的递归方式进行
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
int lastIndex = -1;
for (Integer index : dataMaps.keySet()) {
if (index <= lastIndex) {
continue;
}
Map<Integer, ReadCellData<?>> data = dataMaps.get(index);
Map<Integer, CellExtra> extra = extraMap.get(index);
Object obj = null;
if (extra!=null) {
MergeData mergeData = this.merge(data, context, index, extra);
//删除处理完成的
extraMap.pollFirstEntry();
obj = mergeData.getData();
lastIndex = mergeData.getLastIndex();
} else {
obj= this.convertBean(data, context.readSheetHolder(), index, context);
}
this.doInvoke((T) obj);
}
this.doAfter();
}