告别繁琐样式设置:用EasyExcel 2.2.8的CellStyleModel实现Excel字体样式工业化管理
在Java开发中,Excel报表导出是再常见不过的需求。但每当产品经理拿着设计精美的Excel模板要求开发实现时,那些复杂的字体样式设置往往让开发者头疼不已——标题要加粗、关键数据要标红、注释要斜体、公式要上标...传统做法是在代码里堆砌大量CellStyle设置,不仅难以维护,还让代码变得臃肿不堪。今天,我们就来彻底解决这个痛点。
1. 为什么需要CellStyleModel:传统样式设置的三大痛点
在深入代码之前,我们先看看传统Excel样式设置方式存在的问题。假设我们要为一个销售报表设置以下样式:
- 表头:微软雅黑18号字,加粗,蓝色
- 销售额超过100万的单元格:红色加粗
- 备注列:灰色斜体
- 计算公式:上标显示
1.1 传统实现方式示例
// 典型的传统样式设置代码 CellStyle headerStyle = workbook.createCellStyle(); Font headerFont = workbook.createFont(); headerFont.setFontName("微软雅黑"); headerFont.setFontHeightInPoints((short)18); headerFont.setBold(true); headerFont.setColor(IndexedColors.BLUE.getIndex()); headerStyle.setFont(headerFont); CellStyle highlightStyle = workbook.createCellStyle(); Font highlightFont = workbook.createFont(); highlightFont.setColor(IndexedColors.RED.getIndex()); highlightFont.setBold(true); highlightStyle.setFont(highlightFont); // 更多样式定义...这种写法存在三个明显问题:
- 代码重复:每个样式都需要完整定义Font和CellStyle
- 难以复用:样式逻辑与业务代码紧耦合
- 维护困难:当设计变更时,需要在代码中多处修改
1.2 CellStyleModel的解决方案
EasyExcel 2.2.8引入的CellStyleModel采用声明式的方式定义样式:
List<CellStyleModel> styles = new ArrayList<>(); // 表头样式 styles.add(CellStyleModel.createFontCellStyleModel( "Sheet1", 0, 0, "微软雅黑", 18D, IndexedColors.BLUE, true, false, Font.U_NONE, Font.SS_NONE, false )); // 高亮样式 styles.add(CellStyleModel.createFontCellStyleModel( "Sheet1", 1, 3, null, null, IndexedColors.RED, true, false, Font.U_NONE, Font.SS_NONE, false ));这种方式将样式定义集中管理,实现了样式与业务逻辑的解耦。
2. CellStyleModel深度解析:两种样式设置策略对比
CellStyleModel提供了两种设置字体样式的方式,各有适用场景。
2.1 一次性设置(原子操作)
CellStyleModel.createFontCellStyleModel( String sheetName, int firstRow, int lastRow, String fontName, Double fontHeight, IndexedColors fontColor, Boolean bold, Boolean italic, Byte underline, Short typeOffset, Boolean strikeout )适用场景:当需要为一个单元格或区域设置完整的样式时。例如,表头通常需要同时设置字体、大小、颜色和加粗。
优势:
- 一次调用完成所有设置
- 保证样式设置的原子性
- 代码简洁明了
示例:
// 设置A1单元格为:宋体、14号、红色、加粗、斜体、单下划线 styles.add(CellStyleModel.createFontCellStyleModel( "Sheet1", 0, 0, "宋体", 14D, IndexedColors.RED, true, true, Font.U_SINGLE, Font.SS_NONE, false ));2.2 分别设置(组合操作)
CellStyleModel还提供了一系列工厂方法,可以单独设置某个样式属性:
| 方法名 | 功能描述 | 示例代码 |
|---|---|---|
| createFontNameCellStyleModel | 设置字体名称 | createFontNameCellStyleModel("Sheet1", 0, 0, "楷体") |
| createFontHeightCellStyleModel | 设置字体大小 | createFontHeightCellStyleModel("Sheet1", 0, 0, 16D) |
| createFontColorCellStyleModel | 设置字体颜色 | createFontColorCellStyleModel("Sheet1", 0, 0, IndexedColors.GREEN) |
| createFontBoldCellStyleModel | 设置加粗 | createFontBoldCellStyleModel("Sheet1", 0, 0, true) |
| createFontItalicCellStyleModel | 设置斜体 | createFontItalicCellStyleModel("Sheet1", 0, 0, true) |
| createFontUnderLineCellStyleModel | 设置下划线 | createFontUnderLineCellStyleModel("Sheet1", 0, 0, Font.U_DOUBLE) |
| createFontTypeOffsetCellStyleModel | 设置上标/下标 | createFontTypeOffsetCellStyleModel("Sheet1", 0, 0, Font.SS_SUPER) |
| createFontStrikeoutCellStyleModel | 设置删除线 | createFontStrikeoutCellStyleModel("Sheet1", 0, 0, true) |
适用场景:当需要基于某个基准样式进行微调时。例如,大部分单元格使用默认样式,只有特定条件才添加红色或加粗。
优势:
- 可以灵活组合样式
- 避免重复定义相同属性
- 便于实现样式的增量修改
示例:
// 先设置基础样式 styles.add(CellStyleModel.createFontCellStyleModel( "Sheet1", 1, 100, "微软雅黑", 12D, IndexedColors.BLACK, false, false, Font.U_NONE, Font.SS_NONE, false )); // 然后只对满足条件的单元格添加红色 if(sales > 1000000) { styles.add(CellStyleModel.createFontColorCellStyleModel( "Sheet1", row, col, IndexedColors.RED )); }3. 工程化实践:将样式配置提升到架构层面
真正高效的开发不是每次导出Excel时都重新定义样式,而是建立一套可复用的样式管理系统。下面介绍如何将CellStyleModel集成到企业级项目中。
3.1 样式配置中心化
创建一个ExcelStyleConfig类集中管理所有样式定义:
public class ExcelStyleConfig { private static final String DEFAULT_FONT = "微软雅黑"; public static List<CellStyleModel> getHeaderStyles(String sheetName) { List<CellStyleModel> styles = new ArrayList<>(); styles.add(CellStyleModel.createFontCellStyleModel( sheetName, 0, 0, DEFAULT_FONT, 18D, IndexedColors.DARK_BLUE, true, false, Font.U_NONE, Font.SS_NONE, false )); return styles; } public static List<CellStyleModel> getHighlightStyles( String sheetName, int row, int col) { List<CellStyleModel> styles = new ArrayList<>(); styles.add(CellStyleModel.createFontColorCellStyleModel( sheetName, row, col, IndexedColors.RED )); styles.add(CellStyleModel.createFontBoldCellStyleModel( sheetName, row, col, true )); return styles; } // 更多预定义样式... }3.2 动态样式策略
结合业务规则实现动态样式生成:
public class SalesReportStyleStrategy { public static List<CellStyleModel> generateStyles( String sheetName, List<SalesData> dataList) { List<CellStyleModel> styles = new ArrayList<>(); // 添加表头样式 styles.addAll(ExcelStyleConfig.getHeaderStyles(sheetName)); // 为数据行添加条件样式 for(int i = 0; i < dataList.size(); i++) { SalesData data = dataList.get(i); if(data.getAmount() > 1000000) { styles.addAll(ExcelStyleConfig.getHighlightStyles( sheetName, i+1, 3 )); } if(data.isSpecialNote()) { styles.add(CellStyleModel.createFontItalicCellStyleModel( sheetName, i+1, 5, true )); } } return styles; } }3.3 性能优化建议
当处理大量样式时,考虑以下优化措施:
- 样式合并:将相邻单元格的相同样式合并为一个区域设置
- 缓存重用:对常用样式进行缓存,避免重复创建
- 懒加载:只在首次使用时初始化样式配置
public class StyleCache { private static final Map<String, List<CellStyleModel>> CACHE = new ConcurrentHashMap<>(); public static List<CellStyleModel> getStyles(String styleKey) { return CACHE.computeIfAbsent(styleKey, k -> { switch(k) { case "HEADER": return ExcelStyleConfig.getHeaderStyles(""); case "HIGHLIGHT": return ExcelStyleConfig.getHighlightStyles("", 0, 0); default: return Collections.emptyList(); } }); } }4. 实战案例:销售报表导出完整实现
让我们通过一个完整的销售报表导出示例,展示如何在实际项目中使用CellStyleModel。
4.1 定义数据模型
@Data public class SalesReportVO { @ExcelProperty("区域") private String region; @ExcelProperty("销售代表") private String salesPerson; @ExcelProperty("销售额") private BigDecimal amount; @ExcelProperty("完成率") private String completionRate; @ExcelProperty("备注") private String comment; // 判断是否需要高亮显示 public boolean isHighlight() { return amount.compareTo(new BigDecimal("1000000")) > 0; } // 判断是否为特殊备注 public boolean isSpecialComment() { return comment != null && comment.startsWith("[重要]"); } }4.2 实现样式处理器
public class SalesReportStyleHandler extends CustomCellStyleHandler { private final List<SalesReportVO> dataList; public SalesReportStyleHandler(List<CellStyleModel> cellStyleModels, List<SalesReportVO> dataList) { super(cellStyleModels); this.dataList = dataList; } @Override protected List<CellStyleModel> getCellStyleModels(WriteSheetHolder writeSheetHolder) { List<CellStyleModel> styles = new ArrayList<>(); // 添加预定义样式 styles.addAll(StyleCache.getStyles("HEADER")); // 添加条件样式 for(int i = 0; i < dataList.size(); i++) { SalesReportVO item = dataList.get(i); if(item.isHighlight()) { styles.addAll(StyleCache.getStyles("HIGHLIGHT") .stream() .map(m -> m.copyToRowCol(i+1, m.getLastCol())) .collect(Collectors.toList())); } if(item.isSpecialComment()) { styles.add(CellStyleModel.createFontItalicCellStyleModel( writeSheetHolder.getSheetName(), i+1, 4, true )); } } return styles; } }4.3 组装导出逻辑
@GetMapping("/exportSalesReport") public void exportSalesReport(HttpServletResponse response) { try { // 1. 准备数据 List<SalesReportVO> data = salesService.getReportData(); // 2. 设置响应头 String fileName = URLEncoder.encode("销售报表.xlsx", "UTF-8"); response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName); // 3. 构建ExcelWriter ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) .registerWriteHandler(new SalesReportStyleHandler(new ArrayList<>(), data)) .build(); // 4. 写入数据 WriteSheet writeSheet = EasyExcel.writerSheet("销售数据") .head(SalesReportVO.class) .build(); excelWriter.write(data, writeSheet); // 5. 完成写入 excelWriter.finish(); } catch (Exception e) { log.error("导出销售报表失败", e); throw new RuntimeException("导出失败"); } }4.4 样式效果说明
最终生成的Excel将包含以下样式特征:
- 表头行:深蓝色、加粗、18号微软雅黑
- 销售额超过100万的行:红色字体、加粗
- 标记为[重要]的备注:斜体显示
- 其他单元格:默认样式(黑色、12号、正常字体)
这种实现方式将样式逻辑与业务逻辑完全分离,当产品经理要求调整样式时,我们只需要修改ExcelStyleConfig中的定义,而不需要改动业务代码。