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

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

文章目录

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

1. 定义注解

java 复制代码
import java.lang.annotation.*;

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

2. 实体类应用注解

java 复制代码
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. 动态导出工具类

java 复制代码
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;
    }
}
相关推荐
liguojun20251 小时前
篮球馆自动计时收费系统:从规则配置到自动结算的全流程拆解
java·大数据·运维·人工智能·物联网·1024程序员节
952362 小时前
RabbitMQ-基础操作
java·spring boot·分布式·后端·spring·rabbitmq
QN1幻化引擎2 小时前
给 AI 做一次「意识体检」——基于 QN1 幻化引擎的灵鉴意识识别框架与 DalinX V5 实测
大数据·数据结构·人工智能·算法·架构
拂拉氏2 小时前
【知识讲解】 AVL树从基本成员的介绍到核心接口的实现(插入、判断、删除等等)
数据结构·算法·avl树
卓怡学长2 小时前
w266基于spring boot + vue 圣地延安美食乐享系统
java·数据库·vue.js·spring boot·spring·intellij-idea
C++、Java和Python的菜鸟3 小时前
第7章 Java高级技术
java·开发语言
程序员佳佳3 小时前
模型网关灰度不是调百分比:把放量、观测和回滚做成账本
java·数据库·人工智能·redis·gpt·aigc·embedding
得闲喝茶3 小时前
跨表数据匹配——VLOOKUP、XLOOKUP
大数据·数据库·笔记·信息可视化·数据分析·excel
Listen·Rain4 小时前
Springboot整合Ollama实现调用本地多模型
java·spring boot·后端
霸道流氓气质4 小时前
Java 工程师 AI 智能体(Agent)完整学习路线
java·人工智能·学习