⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7509398334688460812 「天天摸鱼的java工程师」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

各位打工人好,我是那个在 CRUD 江湖摸爬滚打七年的老码农。还记得刚入行时,一听到 “Excel 导出” 就条件反射地冒冷汗 ——POI 那套 API 跟迷宫似的,CellStyle 能把人调得眼冒金星,大数据量导出时内存飙到 90% 的恐惧至今难忘。直到去年被产品经理逼到墙角时,发现了 Alibaba 的 EasyExcel,从此打开新世界的大门。

今天就把这套 “导出救命锦囊” 分享给大家,顺便穿插点当年踩坑的段子,让咱们在笑声中搞定 Excel 导出。

一、先给项目把 EasyExcel 的 “脉”—— 环境整合篇

1. 引入依赖:别让 Maven 耍小脾气

首先在pom.xml里加依赖,这里得注意版本兼容性。我曾试过用最新版 EasyExcel 配老 Spring Boot,结果像小孩打架似的报错不断。建议直接抄作业:

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

加完记得刷新 Maven,这玩意儿有时候像闹情绪的女朋友,得哄着来。

2. 创建实体类:给数据贴 “xxx”

咱得先定义 Excel 里每一列的数据结构,就像给每个字段发一张 “座位牌”。比如导出用户信息:

@Data
public class UserExcelVO {
// 这里是表头名称,宽度设置成20,就像给单元格定制合适的衣服尺码
@ExcelProperty(value = "用户ID", index = 0)
private Long userId;

// 用转换器把日期变成"yyyy-MM-dd"格式,不然Excel会显示成奇怪的数字
@ExcelProperty(value = "注册时间", index = 1)
@DateTimeFormat("yyyy-MM-dd")
private Date registerTime;

// 自定义转换器,把性别0/1转成"男""女",不然产品经理会追着你改需求
@ExcelProperty(value = "性别", index = 2)
@Converter(SexConverter.class)
private Integer sex;
}

这里的@ExcelProperty就像给数据贴标签,index是列顺序,可别标错号,不然数据错位时你会怀疑人生 —— 我曾把金额和年龄的位置搞反,被财务小姐姐骂得狗血淋头。

3. 编写导出工具类:把重复工作打包成 “瑞士军刀”

把通用导出逻辑封装起来,以后每次导出就像拧瓶盖一样简单。创建EasyExcelUtils:

public class EasyExcelUtils {
public static <T> void exportExcel(HttpServletResponse response,
List<T> dataList,
Class<T> clazz,
String fileName) throws IOException {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 文件名得处理中文,不然下载下来是乱码,别问我怎么知道的(摸了摸头上的包)
fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

// 这里用EasyExcel.write()就像启动一个Excel生成器,简单到让我怀疑人生
EasyExcel.write(response.getOutputStream(), clazz)
.sheet("数据报表")
.doWrite(dataList);
}
}

从此告别每次导出都写重复代码的日子,就像有了自动咖啡机,再也不用手动磨豆子了。

二、实战演练:从 “普通导出” 到 “花里胡哨” 的需求突围

1. 基础导出:先搞定 “刚需”

在 Controller 里调用工具类,就像把食材放进微波炉:

@GetMapping("/exportUser")
public void exportUser(HttpServletResponse response) throws IOException {
List<UserExcelVO> dataList = userService.getUserListForExport(); // 假设这是从数据库查的数据
EasyExcelUtils.exportExcel(response, dataList, UserExcelVO.class, "用户信息表");
}

第一次跑通时,我对着屏幕傻笑了三分钟 —— 七年了,终于不用跟 POI 的 N 个参数较劲了!

2. 复杂表头:当领导突然说 “再加一层分类”

有时候表头需要多级结构,比如 “用户信息” 下分 “基本信息”” 联系方式 “。这时候需要用@ExcelProperty的数组形式:

@Data
public class ComplexHeaderVO {
@ExcelProperty({"用户信息", "用户ID"})
private Long userId;

@ExcelProperty({"用户信息", "姓名"})
private String userName;

@ExcelProperty({"联系方式", "手机号"})
private String phone;

@ExcelProperty({"联系方式", "邮箱"})
private String email;
}

看着生成的两层表头,突然想起当年被产品经理临时加需求的夜晚,而现在,EasyExcel 让我能优雅应对,甚至想主动问:”还有什么花样?爸爸接得住!”

3. 合并单元格:玩一把 “Excel 拼图游戏”

比如导出报表时需要合并相同内容的单元格,这时候得自定义CellWriteHandler。举个例子,合并连续相同的部门名称:

public class MergeCellHandler implements CellWriteHandler {
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
// 这里省略具体实现,核心是通过行号和列号判断是否合并
// 就像拼拼图,找到相同的部分粘在一起
}
}

在导出时注册这个处理器:

EasyExcel.write(...)
.registerWriteHandler(new MergeCellHandler())
.doWrite(...);

第一次实现合并单元格时,我兴奋地在工位上哼起了《爱拼才会赢》,毕竟以前用 POI 写合并代码能写满两屏,现在轻松多了。

4. 自定义格式:让数据 “穿上漂亮衣服”

比如金额需要显示成 “¥1,000.00”,日期要显示成 “2025 年 5 月 29 日”。除了前面提到的@DateTimeFormat,数值格式可以用@NumberFormat:

@ExcelProperty("金额")
@NumberFormat("#,##0.00")
private Double amount;

看着导出的规范格式,突然想起当年因为金额格式不对被财务打回 13 次的惨案,现在终于能扬眉吐气了。

5. 大数据量导出:别让内存 “撑破肚皮”

当数据量超过 10 万条时,直接导出会把内存撑爆,这时候得用流式处理。EasyExcel 贴心地支持分页导出,每次查 1000 条,分批次写入:

EasyExcel.write(response.getOutputStream(), UserExcelVO.class)
.sheet("大数据报表")
.registerWriteHandler(...) // 可选的样式处理器
.doWrite(new AnalysisContext() -> {
// 这里每次调用获取一页数据,直到没有数据为止
List<UserExcelVO> pageData = userService.getPageData(analysisContext.readRowHolder().getRowIndex());
return pageData;
});

记得当年用 POI 导出 20 万条数据,服务器直接内存溢出,被运维大哥拎着键盘来问罪,现在有了流式处理,再也不怕大数据量了。

三、避坑指南:这些 “暗礁” 别踩

1. 依赖冲突:当 Maven 开始 “闹别扭”

如果引入了旧版 POI 依赖,可能会和 EasyExcel 的 POI 版本冲突。这时候别慌,用mvn dependency:tree命令排查,然后在pom.xml里用排除冲突项,就像哄孩子一样,把捣乱的依赖赶走。

2. 注解优先级:别让 “标签” 打架

@ExcelProperty可以写在字段上或方法上,建议统一写在字段上,不然容易混乱。我曾在一个类里混合使用,结果导出时字段顺序乱成一锅粥,debug 了两小时才发现问题。

3. 样式设置:别把 Excel 变成 “花脸猫”

虽然 EasyExcel 支持自定义样式,但别过度使用,比如给每个单元格设置不同颜色,导出的 Excel 可能打不开。样式设置要适度,就像化妆,自然美才是真的美。

四、总结:从 “被 Excel 支配” 到 “掌控导出自由”

折腾了七年 Excel 导出,从 POI 的磕磕绊绊到 EasyExcel 的丝滑体验,不得不感叹开源工具的伟大。现在每次接到导出需求,我都能淡定地说:”放马过来吧,不管是复杂表头、大数据量还是格式要求,咱这工具都能搞定。”

最后送大家一句口诀:EasyExcel 真的妙,注解一标数据跳,工具类里封装好,导出从此没烦恼。复杂需求别害怕,处理器来把忙帮,踩过的坑我来扛,各位兄弟放心上!

如果你在整合过程中遇到什么奇怪的问题,欢迎留言交流 —— 毕竟我这七年攒了一箩筐的导出故事,咱们慢慢唠。现在,打开你的 IDE,试试用 EasyExcel 导出第一份数据吧,相信我,你会爱上这种轻松的感觉!

文章目录
  1. 1. 一、先给项目把 EasyExcel 的 “脉”—— 环境整合篇
    1. 1.1. 1. 引入依赖:别让 Maven 耍小脾气
    2. 1.2. 2. 创建实体类:给数据贴 “xxx”
    3. 1.3. 3. 编写导出工具类:把重复工作打包成 “瑞士军刀”
  2. 2. 二、实战演练:从 “普通导出” 到 “花里胡哨” 的需求突围
    1. 2.1. 1. 基础导出:先搞定 “刚需”
    2. 2.2. 2. 复杂表头:当领导突然说 “再加一层分类”
    3. 2.3. 3. 合并单元格:玩一把 “Excel 拼图游戏”
    4. 2.4. 4. 自定义格式:让数据 “穿上漂亮衣服”
    5. 2.5. 5. 大数据量导出:别让内存 “撑破肚皮”
  3. 3. 三、避坑指南:这些 “暗礁” 别踩
    1. 3.1. 1. 依赖冲突:当 Maven 开始 “闹别扭”
    2. 3.2. 2. 注解优先级:别让 “标签” 打架
    3. 3.3. 3. 样式设置:别把 Excel 变成 “花脸猫”
  4. 4. 四、总结:从 “被 Excel 支配” 到 “掌控导出自由”