EasyExcel读大数据Excel必须用监听器,因其采用SAX式流式解析,内存稳定在几MB;不用则全量加载致OOM。监听器需继承AnalysisEventListener,重写invoke()和doAfterAllAnalysed(),分批刷盘防OOM,并注意字段映射、事务及性能瓶颈。

EasyExcel读大数据Excel为什么必须用监听器
因为不走监听器模式,EasyExcel会把整张表全加载进内存——10万行、每行50列的Excel,轻松吃掉500MB堆内存,接着就是OutOfMemoryError: Java heap space。监听器本质是SAX式流式解析,边读边处理,内存占用稳定在几MB级别。
常见错误现象:read()直接传List.class或用readSync();结果小文件能跑通,一上生产就OOM。
- 监听器必须继承
AnalysisEventListener,不能只实现接口(缺泛型推导和生命周期钩子) - 重写
invoke()处理单行数据,doAfterAllAnalysed()收尾(比如批量插入数据库) -
invoke()里别缓存所有data到成员变量——这是新手最常踩的坑,等于又回到全量加载
监听器里怎么安全做数据库批量插入
单行insert太慢,但全攒到doAfterAllAnalysed()又可能OOM。折中方案是“分批刷盘”:每积满1000行执行一次JdbcTemplate.batchUpdate()或MyBatis的SqlSessionExecutor。
使用场景:导入用户主数据、订单明细等需落库的业务。
立即学习“Java免费学习笔记(深入)”;
- 在监听器里声明
private List<User> cachedData = new ArrayList<>(1000),容量预设避免频繁扩容 -
invoke()里add后判断cachedData.size() == 1000,触发batchInsert(cachedData)并cachedData.clear() -
doAfterAllAnalysed()里补上剩余不足1000条的数据——漏这步会导致最后几百行丢数据 - 注意事务:Spring默认传播行为下,
doAfterAllAnalysed()不在事务内,得用@Transactional包一层方法或手动管理
读取时字段映射失败的三个高频原因
EasyExcel靠注解或头匹配找字段,一旦错位、空格、类型不兼容,invoke()里拿到的就是null或转换异常,但日志未必报错——数据静默丢失。
常见错误现象:String字段读成null;Date字段抛DateTimeParseException;数字列被当科学计数法字符串(如"1.23456789E8")
- 检查Excel头行是否含不可见字符(用
head.get(0).trim().length()验证),建议用@ExcelProperty(index = 0)硬绑定列序而非名称 -
Date字段加@DateTimeFormat("yyyy-MM-dd"),且Excel单元格格式必须是日期类型(不是文本) - 数字列若可能超int范围,实体类字段用
Long或BigDecimal,避免自动转Double再截断 - 监听器构造时传
new AnalysisEventListener<User>(){...}要显式指定泛型,否则反射取不到User字段元信息
性能卡在IO还是解析?用JFR快速定位
读100MB Excel耗时2分钟,不一定是EasyExcel慢——可能是磁盘IO瓶颈(尤其网络盘)、或Excel本身有隐藏公式/样式拖慢解析。
性能影响点:EasyExcel.read()底层用Apache POI的SXSSF,但POI对xlsx的共享字符串表(SharedStringsTable)解析较重;复杂样式越多,CPU消耗越明显。
- 用JDK自带
jfr start --duration=60s --filename=recording.jfr录下读取过程,打开Java Mission Control看热点方法:如果org.apache.poi.xssf.model.SharedStringsTable占比高,说明文件结构问题 - 临时提速:读取前用
WorkbookFactory.create(new File(path), null, true)跳过样式(EasyExcel不直接暴露该开关,需自定义ExcelReaderBuilder注入WorkbookFactory) - 真正大数据场景(千万行+),别死磕Excel——推动上游改用CSV或数据库直连,Excel只是临时交互格式
监听器看着简单,但invoke()里的每一行处理逻辑,都得按高并发、低延迟的代码标准来写。没人会告诉你,一个System.out.println()在百万行循环里能让耗时翻倍。










