Java操作Excel神器,easyExcel 源码分析
Posted 香菜聊游戏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java操作Excel神器,easyExcel 源码分析相关的知识,希望对你有一定的参考价值。
easyExcel简介
项目有个需求就是需要上传Excel,然后读取数据,进行一系列的操作,很久之前就看到EasyExcel,但是一直没用过,今天试着用一下。
Java领域解析、操作Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。
easyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。
64M内存1分钟内读取75M(46W行25列)的Excel(当然还有急速模式能更快,但是内存占用会在100M多一点)
官方说明:Alibaba Easy Excel - 简单、省内存的Java解析Excel工具 | 读Excel
类的概念
AnalysisContext 核心接口,也是整个操作期间的上下文。
ExcelWriter 写入Excel的类
ExcelReader 读取Excel的类
Cell 对应一行数据 sheet 对应单个表格
简单读取
因为我项目中涉及的就是Excel的读取,所以这边就简单说一下读取,也是来自官方的例子。
每行数据对应的数据结构:
@Data
public class DemoData
private String string;
private Date date;
private Double doubleData;
监听器,读取到的每一行数据都会进入到invoke回调
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData>
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<DemoData> list = new ArrayList<DemoData>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoDAO demoDAO;
public DemoDataListener()
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
public DemoDataListener(DemoDAO demoDAO)
this.demoDAO = demoDAO;
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as @link AnalysisContext#readRowHolder()
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context)
LOGGER.info("解析到一条数据:", JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT)
saveData();
// 存储完成清理 list
list.clear();
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context)
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!");
/**
* 加上存储数据库
*/
private void saveData()
LOGGER.info("条数据,开始存储数据库!", list.size());
demoDAO.save(list);
LOGGER.info("存储数据库成功!");
读取数据发起点:
/**
* 最简单的读
* <p>1. 创建excel对应的实体对象 参照@link DemoData
* <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照@link DemoDataListener
* <p>3. 直接读即可
*/
@Test
public void simpleRead()
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法1:
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
// 写法2:
fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
ReadSheet readSheet = EasyExcel.readSheet(0).build();
excelReader.read(readSheet);
// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
excelReader.finish();
原理解析
简单理解:在读取的过程中每次读取一行数据,调用Listener中的invoke函数,把数据给调用者进行解析,处理。
源码跟踪:
使用上面的demo,
第一步在读取的地方打上断点,
第二步在invoke的地方打上断点
看下堆栈的调用顺序,然后从下往上将每一个堆栈点一下看一下代码 的执行顺序,大概就知道这个执行流程。
下面摘抄一些核心代码
com.alibaba.excel.analysis.ExcelAnalyserImpl#analysis
前面的都是填充上下文数据,这里才开始真正的读取Excel
中间扫描Excel的过程不关注了,基本上就是对数据的读取,比较繁琐。
com.alibaba.excel.read.processor.DefaultAnalysisEventProcessor#endRow
这里是读取一行结束之后的处理
对数据的处理 com.alibaba.excel.read.processor.DefaultAnalysisEventProcessor#dealData
到这里已经能看到调用了设置的listener,在读到这里的时候我很好奇isData的设置,也就是表头是有多少行是怎么处理的。在我的代码中时没有做设置的。
默认的是第一行是表头
总结:
easyexcel 是真的简单
以上是关于Java操作Excel神器,easyExcel 源码分析的主要内容,如果未能解决你的问题,请参考以下文章
poi和easyExcel基于Java操作Excel学习笔记