需排除Spring Boot自带POI并显式声明5.2.4版本,读写大文件须分片flush,日期数字字段需配@DateTimeFormat/@NumberFormat,表头匹配优先用value+headRowNumber校验。

EasyExcel 依赖怎么加才不冲突
Spring Boot 项目里直接加 easyexcel 依赖,大概率会和已有的 poi 版本打架——尤其你项目里还用了 poi-ooxml 4.1.2 或更老的版本。EasyExcel 3.x 默认要求 POI 5.2.4+,但 Spring Boot 2.7 自带的 poi 是 4.1.2,硬塞进去会报 NoClassDefFoundError: org/apache/poi/xxssf/usermodel/XSSFWorkbook 这类错。
实操建议:
立即学习“Java免费学习笔记(深入)”;
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 用
spring-boot-starter-parent2.7.x 的项目,必须排除父 POM 自带的poi,显式声明兼容版本:<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.3.2</version> <exclusions> <exclusion> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> </exclusion> <exclusion> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.4</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.4</version> </dependency> - 别用
easyexcel-spring-boot-starter—— 它封装太死,升级慢,且对自定义Converter和监听器支持差 - 如果项目还用了
Apache POI做其他 Excel 操作(比如生成图表),确保所有 POI 相关依赖版本统一,否则运行时类加载顺序一变就崩
读 Excel 时日期、数字类型自动转成奇怪的 double
EasyExcel 默认把 Excel 单元格按原始类型读:日期变成 java.util.Date,但如果你没配好格式或用了 @ExcelProperty 不带类型声明,它常会把日期当数字读成 Double(比如 44562.0),数字列也可能被识别成科学计数法字符串或 double。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 读取含日期列时,字段类型必须是
java.util.Date或java.time.LocalDateTime,并配上@DateTimeFormat:@DateTimeFormat("yyyy-MM-dd")<br>private Date createTime; - 数字列想保持整型,别用
Integer接——Excel 里哪怕显示“123”,底层可能是浮点存储。稳妥做法是字段用BigDecimal,再配合@NumberFormat控制精度:@NumberFormat("#.##")<br>private BigDecimal amount; - 如果要完全关闭自动类型推断,读的时候传入
new AnalysisEventListener<YourClass>() { ... },并在invoke方法里手动调用readRowHolder.getCellDataList()拿原始CellData,自己解析
写大文件时内存爆掉或导出卡死
EasyExcel 默认用 SAX 模式读、DOM 模式写,写 10 万行以上数据不设限,JVM 很快 OOM;而读超大文件(比如 50MB+)时如果监听器里做了耗时操作(如查数据库、发 HTTP 请求),也会卡住主线程导致超时。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 写大文件必须用
WriteSheet+table分批写,每 1 万行 flush 一次:EasyExcel.write(outputStream, YourData.class) .sheet("数据表") .doWrite(dataList); // 别一股脑传百万条改成循环分片:for (List<YourData> batch : Lists.partition(allData, 10000)) { EasyExcel.write(outputStream, YourData.class) .sheet("数据表") .doWrite(batch); } - 读大文件时,监听器里的
invoke方法务必轻量——禁止在里头做 DB 写入、远程调用;应先缓存到本地队列,另起线程批量处理 - JVM 启动参数至少加
-Xmx2g -XX:+UseG1GC,否则默认堆太小,写 5 万行就可能触发频繁 GC
@ExcelProperty 的 index 和 name 到底该用哪个
列位置变动是 Excel 解析最常见翻车点。用 index = 0 看似简单,但 Excel 表头增删列后,所有 index 全乱;用 value = "用户名" 看似灵活,但中英文空格、全角半角、隐藏字符一多,匹配就失败,日志里只打一句 Can not find column,很难定位。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 优先用
value(即表头文字),但必须配合headRowNumber = 1显式指定表头在哪一行,并在读之前校验表头是否完整:public class MyListener extends AnalysisEventListener<YourData> { @Override public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) { if (!headMap.containsValue("订单号")) { throw new IllegalArgumentException("缺少必要表头:订单号"); } } } - 真要靠列序号(比如模板固定、不允许改表头),就别混用
index和value——一个类里要么全用index,要么全用value,否则 EasyExcel 会行为不一致 - 表头文字含特殊符号(如括号、斜杠)时,在
value里写全,别简写;Excel 复制过来的文本常带不可见 Unicode 字符,用headMap.values().forEach(System.out::println)打印出来肉眼核对

















