当前位置: 首页 > news >正文

使用注解动态映射:根据实体List列表动态生成Excel文件

我们一般通过POI来生成对应的Excel文件,绝大多数情况是需要手动编写单元格内容,然后顺序填充值,今天我们将动态根据实体来生成Excel表头,同时自动填充内容。

文章目录

  • 1. 定义注解
  • 2. 实体类应用注解
  • 3. 动态导出工具类

1. 定义注解

import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelColumn {String name();          // Excel列名int order();            // 列顺序(从0开始)String format() default ""; // 格式(如日期、数字格式)
}

2. 实体类应用注解

public class Employee {@ExcelColumn(name = "员工ID", order = 0)private Integer id;@ExcelColumn(name = "姓名", order = 1)private String name;@ExcelColumn(name = "部门", order = 2)private String department;@ExcelColumn(name = "薪资", order = 3, format = "#,##0.00")private Double salary;@ExcelColumn(name = "入职日期", order = 4, format = "yyyy-MM-dd")private Date hireDate;// getter/setter省略...
}

3. 动态导出工具类

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;public class DynamicExcelExporter {public static <T> void export(List<T> dataList, OutputStream outputStream) throws IOException {if (dataList == null || dataList.isEmpty()) {throw new IllegalArgumentException("数据列表不能为空");}Class<?> clazz = dataList.get(0).getClass();Field[] fields = clazz.getDeclaredFields();// 获取带注解的字段并按order排序List<Field> annotatedFields = Arrays.stream(fields).filter(f -> f.isAnnotationPresent(ExcelColumn.class)).sorted(Comparator.comparingInt(f -> f.getAnnotation(ExcelColumn.class).order())).toList();try (Workbook workbook = new XSSFWorkbook()) {Sheet sheet = workbook.createSheet("Sheet1");// 1. 动态生成表头createDynamicHeader(workbook, sheet, annotatedFields);// 2. 动态填充数据fillDynamicData(workbook, sheet, dataList, annotatedFields);// 3. 自动调整列宽for (int i = 0; i < annotatedFields.size(); i++) {sheet.autoSizeColumn(i);}workbook.write(outputStream);}}private static void createDynamicHeader(Workbook workbook, Sheet sheet, List<Field> fields) {Row headerRow = sheet.createRow(0);CellStyle headerStyle = createHeaderStyle(workbook);for (int i = 0; i < fields.size(); i++) {ExcelColumn annotation = fields.get(i).getAnnotation(ExcelColumn.class);Cell cell = headerRow.createCell(i);cell.setCellValue(annotation.name());cell.setCellStyle(headerStyle);}}private static <T> void fillDynamicData(Workbook workbook, Sheet sheet, List<T> dataList, List<Field> fields) {for (int rowIdx = 0; rowIdx < dataList.size(); rowIdx++) {T item = dataList.get(rowIdx);Row row = sheet.createRow(rowIdx + 1);for (int colIdx = 0; colIdx < fields.size(); colIdx++) {Field field = fields.get(colIdx);field.setAccessible(true);try {Object value = field.get(item);ExcelColumn annotation = field.getAnnotation(ExcelColumn.class);createCell(workbook, row, colIdx, value, annotation.format());} catch (IllegalAccessException e) {createCell(workbook, row, colIdx, "", null);}}}}private static void createCell(Workbook workbook, Row row, int column, Object value, String format) {Cell cell = row.createCell(column);if (value == null) {cell.setCellValue("");} else if (value instanceof Number) {cell.setCellValue(((Number) value).doubleValue());if (format != null && !format.isEmpty()) {CellStyle style = workbook.createCellStyle();style.setDataFormat(workbook.createDataFormat().getFormat(format));cell.setCellStyle(style);}} else if (value instanceof Boolean) {cell.setCellValue((Boolean) value);} else if (value instanceof Date) {cell.setCellValue((Date) value);CellStyle style = workbook.createCellStyle();String dateFormat = format != null ? format : "yyyy-MM-dd";style.setDataFormat(workbook.createDataFormat().getFormat(dateFormat));cell.setCellStyle(style);} else {cell.setCellValue(value.toString());}}private static CellStyle createHeaderStyle(Workbook workbook) {CellStyle style = workbook.createCellStyle();Font font = workbook.createFont();font.setBold(true);font.setColor(IndexedColors.WHITE.getIndex());style.setFont(font);style.setFillForegroundColor(IndexedColors.BLUE.getIndex());style.setFillPattern(FillPatternType.SOLID_FOREGROUND);style.setAlignment(HorizontalAlignment.CENTER);// 设置边框style.setBorderBottom(BorderStyle.THIN);style.setBorderTop(BorderStyle.THIN);style.setBorderLeft(BorderStyle.THIN);style.setBorderRight(BorderStyle.THIN);return style;}
}

在这里插入图片描述

相关文章:

  • 2025-05-20 模型下载--文本向量化--Faiss检索
  • STL中list的模拟
  • 链表原理与实现:从单链表到LinkedList
  • Gin--Blog项目-flags文件解析
  • OpenCV 人脸识别:从基础到实践全解析
  • HarmonyOS5云服务技术分享--云缓存快速上手指南
  • vue2+webpack环境变量配置
  • 开源一个记账软件,支持docker一键部署
  • 详解ip地址、子网掩码、网关、广播地址
  • ArcObjects学习教程
  • CouchDB 可观测最佳实践
  • Centos上搭建 OpenResty
  • 什么是 AI 人工智能?什么是机器学习?什么是深度学习?三者啥关系
  • 企业级 Go 多版本环境部署指南-Ubuntu CentOS Rocky全兼容实践20250520
  • 利用朴素贝叶斯对UCI 的 mushroom 数据集进行分类
  • CentOS 7上部署BIND9 DNS服务器指南
  • RustDesk CentOS自建中继节点
  • CentOS 7上搭建高可用BIND9集群指南
  • 将 /dev/vdb1 的空间全部合并到 /dev/mapper/centos-root(即扩展 CentOS 的根分区)
  • MyBatis 动态 SQL 标签详解教程:_set_、_trim_、_sql_、_choose_、_when_
  • 杭州3宗住宅用地收金42.49亿元,最高溢价率51.38%
  • 财政部:4月份中央收入增长1.6%,今年以来首月实现正增长
  • “敌人已经够多了”,菲总统马科斯:愿与杜特尔特家族和解
  • 黄仁勋的新逻辑:从“卖铲人”到“全球AI基建运营商”
  • 让中小学生体验不同职业,上海中高职院校提供超5万个体验名额
  • 全总联合六部门印发工作指引,共保劳动者合法权益