0%

EasyExcel钩子

EasyExcel钩子(Handler)机制:深度定制 Excel 写入过程

EasyExcel 作为高效的 Excel 处理框架,不仅提供了简单的读写 API,还通过钩子机制(Handler) 允许开发者在 Excel 写入的关键节点(如 Workbook 创建、Sheet 初始化、行 / 单元格生成等)插入自定义逻辑。本文将详细解析 EasyExcel 中的四大钩子接口(Workbook、Sheet、Row、Cell),并通过实例演示如何利用钩子实现复杂需求(如自定义样式、数据校验、动态调整结构等)。

钩子机制的核心作用

钩子机制(Handler)是 EasyExcel 提供的扩展点,允许开发者在 Excel 写入的生命周期节点中嵌入自定义代码,实现以下功能:

  • 自定义样式(如表头加粗、奇数行变色、特定单元格高亮);
  • 数据校验(如单元格值超出范围时报错或自动修正);
  • 动态调整结构(如根据数据内容新增列、合并单元格);
  • 资源管理(如在 Workbook 关闭后释放临时资源)。

EasyExcel 将写入过程划分为四个层级的生命周期,对应四类钩子接口,层级关系如下:

1
Workbook(工作簿) → Sheet(工作表) → Row(行) → Cell(单元格)  

四大钩子接口详解

WorkbookWriteHandler:工作簿级钩子

作用于整个 Excel 工作簿的创建前后及销毁阶段,用于全局配置(如设置工作簿属性、加密文件等)。

核心方法
方法名 触发时机 用途示例
beforeWorkbookCreate 工作簿创建前 配置工作簿全局参数(如版本)
afterWorkbookCreate 工作簿创建后 设置工作簿加密密码
afterWorkbookDispose 工作簿所有操作完成并关闭后 释放全局资源

SheetWriteHandler:工作表级钩子

作用于单个 Sheet 的创建前后,用于配置 Sheet 特性(如设置默认列宽、隐藏 Sheet 等)。

核心方法
方法名 触发时机 用途示例
beforeSheetCreate Sheet 创建前 指定 Sheet 索引、名称
afterSheetCreate Sheet 创建后 设置默认列宽、冻结首行

RowWriteHandler:行级钩子

作用于行的创建前后及销毁阶段,用于行级定制(如行高调整、行样式统一等)。

核心方法
方法名 触发时机 用途示例
beforeRowCreate 行创建前 预设行高、判断是否需要跳过此行
afterRowCreate 行创建后 调整行高、设置行背景色
afterRowDispose 行所有操作完成后 行数据校验、记录行索引

CellWriteHandler:单元格级钩子

作用于单元格的创建、数据转换及销毁阶段,是最常用的钩子,用于单元格样式、数据处理等细节定制。

核心方法
方法名 触发时机 用途示例
beforeCellCreate 单元格创建前 预设单元格类型(文本 / 数字)
afterCellCreate 单元格创建后 设置单元格边框、对齐方式
afterCellDataConverted 单元格数据转换为 Excel 格式后 修正数据格式(如日期格式化)
afterCellDispose 单元格所有操作完成后 高亮特定值(如错误数据标红)

实战:自定义钩子实现复杂需求

以下通过三个典型案例,演示如何使用钩子接口解决实际问题。

案例 1:表头样式定制(使用 CellWriteHandler)

需求:表头文字加粗、背景色设为浅灰色,对齐方式居中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import com.alibaba.excel.write.handler.CellWriteHandler;  
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.*;

public class HeaderStyleHandler implements CellWriteHandler {

@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
// 仅处理表头单元格(isHead 为 true)
if (Boolean.TRUE.equals(isHead)) {
Workbook workbook = writeSheetHolder.getWorkbook();
CellStyle headerStyle = workbook.createCellStyle();

// 1. 设置背景色(浅灰色)
headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

// 2. 文字加粗
Font font = workbook.createFont();
font.setBold(true);
headerStyle.setFont(font);

// 3. 水平和垂直居中
headerStyle.setAlignment(HorizontalAlignment.CENTER);
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);

// 应用样式到表头单元格
cell.setCellStyle(headerStyle);
}
}
}

使用方式:在写入时注册钩子

1
2
3
4
EasyExcel.write("表头样式定制.xlsx", UserInfo.class)  
.registerWriteHandler(new HeaderStyleHandler()) // 注册单元格钩子
.sheet("用户表")
.doWrite(dataList);

案例 2:奇数行背景色高亮(使用 RowWriteHandler)

需求:表格中奇数行背景色设为淡蓝色,增强可读性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import com.alibaba.excel.write.handler.RowWriteHandler;  
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.*;

public class OddRowHighlightHandler implements RowWriteHandler {

@Override
public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
Row row, Integer relativeRowIndex, Boolean isHead) {
// 跳过表头(isHead 为 true 时)
if (Boolean.TRUE.equals(isHead)) {
return;
}

// 行索引从 0 开始,奇数行(1、3、5...)高亮
int rowIndex = row.getRowNum();
if (rowIndex % 2 == 1) {
Workbook workbook = writeSheetHolder.getWorkbook();
CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);

// 为行中所有单元格应用样式
for (Cell cell : row) {
cell.setCellStyle(style);
}
}
}
}

使用方式

1
2
3
4
EasyExcel.write("奇数行高亮.xlsx", OrderInfo.class)  
.registerWriteHandler(new OddRowHighlightHandler()) // 注册行钩子
.sheet("订单表")
.doWrite(orderList);

案例 3:动态设置 Sheet 列宽(使用 SheetWriteHandler)

需求:根据 Sheet 名称自动调整列宽(如 “用户表” 列宽 20,“订单表” 列宽 25)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.alibaba.excel.write.handler.SheetWriteHandler;  
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;

public class DynamicColumnWidthHandler implements SheetWriteHandler {

@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
String sheetName = writeSheetHolder.getWriteSheet().getSheetName();
Sheet sheet = writeSheetHolder.getSheet();

// 根据 Sheet 名称设置默认列宽(1 单位 = 1/256 字符宽度)
if ("用户表".equals(sheetName)) {
sheet.setDefaultColumnWidth(20); // 列宽 20
} else if ("订单表".equals(sheetName)) {
sheet.setDefaultColumnWidth(25); // 列宽 25
} else {
sheet.setDefaultColumnWidth(15); // 默认列宽 15
}
}
}

使用方式

1
2
3
4
5
6
7
8
9
10
11
12
// 多 Sheet 写入时,钩子会作用于每个 Sheet  
ExcelWriter writer = EasyExcel.write("动态列宽.xlsx").build();

// 第一个 Sheet:用户表
WriteSheet userSheet = EasyExcel.writerSheet(0, "用户表").head(UserInfo.class).build();
writer.write(userList, userSheet);

// 第二个 Sheet:订单表
WriteSheet orderSheet = EasyExcel.writerSheet(1, "订单表").head(OrderInfo.class).build();
writer.write(orderList, orderSheet);

writer.finish();

钩子的注册与执行顺序

注册方式

  • 全局注册:通过 EasyExcel.write().registerWriteHandler(handler) 注册,作用于所有 Sheet;
  • 局部注册:通过 WriteSheet.writerSheet().registerWriteHandler(handler) 注册,仅作用于当前 Sheet。
1
2
3
4
5
// 全局钩子(作用于所有 Sheet)  
EasyExcel.write()
.registerWriteHandler(new GlobalHandler())
// 局部钩子(仅作用于当前 Sheet)
.sheet().registerWriteHandler(new SheetSpecificHandler());

执行顺序

  • 同类型钩子(如多个 CellWriteHandler)按注册顺序执行;
  • 不同类型钩子按生命周期顺序执行(Workbook → Sheet → Row → Cell)。

注意事项

  1. 性能考量:钩子会在每个节点触发,复杂逻辑可能影响性能,建议钩子内代码简洁高效;
  2. POI 版本兼容:钩子中使用 POI 原生 API(如 CellStyleFont)时,需注意与 EasyExcel 依赖的 POI 版本匹配;
  3. 资源释放:在钩子中创建的 POI 资源(如 FontCellStyle)无需手动释放,EasyExcel 会在 Workbook 关闭时统一处理;
  4. 调试技巧:通过 relativeRowIndex(相对行索引,从 0 开始)区分表头行与数据行,isHead 判断是否为表头。

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10