0-1手写通用的 Excel 导入/导出工具类

前言

在日常开发中,Excel导入导出是一个非常常见的需求。虽然市面上有Apache POI、EasyExcel等优秀的开源库,但在实际使用中,我们经常遇到以下痛点:

常见痛点分析

Excel导入导出痛点
复杂表头处理困难
多级表头支持不足
动态列配置复杂
大数据量性能问题
样式定制不够灵活
数据验证功能弱
合并单元格
表头样式不一致
嵌套表头
分组表头
运行时决定列
条件显示列
内存占用高
导出速度慢
复杂样式配置
主题定制困难
数据类型校验
业务规则验证

基于这些痛点,本文将带你从零开始,实现一个功能完善、易于使用的通用Excel工具类,并封装成团队内部SDK。

一、工具类设计思路

1.1 核心设计原则

  1. 简单易用:通过注解和配置,最小化使用复杂度
  2. 功能强大:支持复杂表头、动态列、数据验证等高级功能
  3. 性能优异:基于流式处理,支持大数据量导入导出
  4. 扩展性强:支持自定义转换器、验证器、样式等
  5. 类型安全:基于泛型设计,编译时类型检查

1.2 技术选型

  • 核心引擎:Apache POI(成熟稳定)
  • 注解驱动:自定义注解简化配置
  • 流式处理:SXSSFWorkbook/SXSSFSheet处理大数据量
  • 模板引擎:支持Excel模板导出
  • 数据验证:内置常用验证规则

1.3 整体架构

Excel工具类SDK
核心引擎层
注解定义层
转换器层
验证器层
样式层
工具层
Excel导入引擎
Excel导出引擎
模板引擎
ExcelField
ExcelSheet
ExcelIgnore
数据转换器接口
内置转换器
自定义转换器
数据验证接口
内置验证器
自定义验证器
样式定义
主题管理
日期工具
数字工具
文件工具

二、核心注解定义

2.1 字段级别注解 @ExcelField

java 复制代码
package com.example.excel.annotation;

import java.lang.annotation.*;

/**
 * Excel字段注解
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcelField {

    /**
     * 列名(支持多级表头,用"|"分隔)
     * 示例:@ExcelField(name = "基本信息|姓名")
     */
    String name();

    /**
     * 列索引(从0开始)
     */
    int index() default -1;

    /**
     * 列宽(单位:字符宽度)
     */
    int width() default 15;

    /**
     * 数据格式
     */
    String format() default "";

    /**
     * 转换器
     */
    Class<? extends ExcelConverter> converter() default ExcelConverter.class;

    /**
     * 验证器
     */
    Class<? extends ExcelValidator> validator() default ExcelValidator.class;

    /**
     * 是否必填
     */
    boolean required() default false;

    /**
     * 必填错误提示
     */
    String requiredMessage() default "该字段不能为空";

    /**
     * 是否导出
     */
    boolean export() default true;

    /**
     * 是否导入
     */
    boolean import() default true;

    /**
     * 下拉选项(用于数据验证)
     */
    String[] options() default {};

    /**
     * 数据类型
     */
    DataType dataType() default DataType.STRING;

    /**
     * 数据类型枚举
     */
    enum DataType {
        STRING,      // 字符串
        NUMBER,      // 数字
        DATE,        // 日期
        BOOLEAN,     // 布尔
        FORMULA,     // 公式
        IMAGE        // 图片
    }
}

2.2 工作表级别注解 @ExcelSheet

java 复制代码
package com.example.excel.annotation;

import java.lang.annotation.*;

/**
 * Excel工作表注解
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcelSheet {

    /**
     * 工作表名称
     */
    String name() default "Sheet1";

    /**
     * 起始行(从0开始,默认为0)
     */
    int startRow() default 0;

    /**
     * 标题行数
     */
    int titleRows() default 1;

    /**
     * 是否创建表头
     */
    boolean createHeader() default true;

    /**
     * 是否自动调整列宽
     */
    boolean autoSizeColumn() default true;

    /**
     * 表头样式
     */
    String headerStyle() default "HEADER";

    /**
     * 数据样式
     */
    String dataStyle() default "DATA";

    /**
     * 是否启用数据验证
     */
    boolean enableValidation() default true;
}

2.3 忽略注解 @ExcelIgnore

java 复制代码
package com.example.excel.annotation;

import java.lang.annotation.*;

/**
 * Excel忽略注解
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcelIgnore {
}

三、核心功能实现

3.1 转换器接口与实现

java 复制代码
package com.example.excel.converter;

/**
 * Excel数据转换器接口
 */
public interface ExcelConverter {

    /**
     * 导出时转换:Java对象 -> Excel单元格值
     */
    Object exportValue(Object value);

    /**
     * 导入时转换:Excel单元格值 -> Java对象
     */
    Object importValue(Object value);
}

内置转换器实现:

java 复制代码
package com.example.excel.converter.impl;

import com.example.excel.converter.ExcelConverter;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 日期转换器
 */
public class DateConverter implements ExcelConverter {

    private String pattern;

    public DateConverter() {
        this("yyyy-MM-dd");
    }

    public DateConverter(String pattern) {
        this.pattern = pattern;
    }

    @Override
    public Object exportValue(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Date) {
            SimpleDateFormat sdf = new SimpleDateFormat(pattern);
            return sdf.format((Date) value);
        }
        return value.toString();
    }

    @Override
    public Object importValue(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Date) {
            return value;
        }
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(pattern);
            return sdf.parse(value.toString());
        } catch (Exception e) {
            throw new RuntimeException("日期格式错误,期望格式:" + pattern);
        }
    }
}

/**
 * 枚举转换器
 */
public class EnumConverter implements ExcelConverter {

    private Class<? extends Enum> enumClass;

    public EnumConverter(Class<? extends Enum> enumClass) {
        this.enumClass = enumClass;
    }

    @Override
    public Object exportValue(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Enum) {
            return value.toString();
        }
        return value;
    }

    @Override
    public Object importValue(Object value) {
        if (value == null) {
            return null;
        }
        try {
            return Enum.valueOf(enumClass, value.toString());
        } catch (Exception e) {
            throw new RuntimeException("枚举值错误:" + value);
        }
    }
}

/**
 * 字典转换器
 */
public class DictConverter implements ExcelConverter {

    private DictService dictService;
    private String dictType;

    public DictConverter(DictService dictService, String dictType) {
        this.dictService = dictService;
        this.dictType = dictType;
    }

    @Override
    public Object exportValue(Object value) {
        if (value == null) {
            return null;
        }
        // 字典值 -> 字典标签
        return dictService.getLabel(dictType, value.toString());
    }

    @Override
    public Object importValue(Object value) {
        if (value == null) {
            return null;
        }
        // 字典标签 -> 字典值
        return dictService.getValue(dictType, value.toString());
    }
}

3.2 验证器接口与实现

java 复制代码
package com.example.excel.validator;

/**
 * Excel数据验证器接口
 */
public interface ExcelValidator {

    /**
     * 验证数据
     * @param value 待验证的值
     * @param fieldName 字段名
     * @param rowIndex 行号
     * @return 验证结果
     */
    ValidationResult validate(Object value, String fieldName, int rowIndex);

    /**
     * 验证结果
     */
    class ValidationResult {
        private boolean valid;
        private String message;

        public ValidationResult(boolean valid, String message) {
            this.valid = valid;
            this.message = message;
        }

        public static ValidationResult success() {
            return new ValidationResult(true, null);
        }

        public static ValidationResult fail(String message) {
            return new ValidationResult(false, message);
        }

        public boolean isValid() {
            return valid;
        }

        public String getMessage() {
            return message;
        }
    }
}

内置验证器实现:

java 复制代码
package com.example.excel.validator.impl;

import com.example.excel.validator.ExcelValidator;
import java.util.regex.Pattern;

/**
 * 手机号验证器
 */
public class PhoneValidator implements ExcelValidator {

    private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");

    @Override
    public ValidationResult validate(Object value, String fieldName, int rowIndex) {
        if (value == null || value.toString().trim().isEmpty()) {
            return ValidationResult.success(); // 非空验证由required字段控制
        }

        String phone = value.toString().trim();
        if (!PHONE_PATTERN.matcher(phone).matches()) {
            return ValidationResult.fail(
                String.format("第%d行,%s格式错误:必须是11位手机号", rowIndex + 1, fieldName)
            );
        }

        return ValidationResult.success();
    }
}

/**
 * 邮箱验证器
 */
public class EmailValidator implements ExcelValidator {

    private static final Pattern EMAIL_PATTERN = 
        Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");

    @Override
    public ValidationResult validate(Object value, String fieldName, int rowIndex) {
        if (value == null || value.toString().trim().isEmpty()) {
            return ValidationResult.success();
        }

        String email = value.toString().trim();
        if (!EMAIL_PATTERN.matcher(email).matches()) {
            return ValidationResult.fail(
                String.format("第%d行,%s格式错误:邮箱格式不正确", rowIndex + 1, fieldName)
            );
        }

        return ValidationResult.success();
    }
}

/**
 * 身份证验证器
 */
public class IdCardValidator implements ExcelValidator {

    @Override
    public ValidationResult validate(Object value, String fieldName, int rowIndex) {
        if (value == null || value.toString().trim().isEmpty()) {
            return ValidationResult.success();
        }

        String idCard = value.toString().trim();
        if (!isValidIdCard(idCard)) {
            return ValidationResult.fail(
                String.format("第%d行,%s格式错误:身份证号格式不正确", rowIndex + 1, fieldName)
            );
        }

        return ValidationResult.success();
    }

    private boolean isValidIdCard(String idCard) {
        // 简化验证,实际应该更严格
        return idCard.length() == 18 && idCard.matches("^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$");
    }
}

/**
 * 数值范围验证器
 */
public class NumberRangeValidator implements ExcelValidator {

    private double min;
    private double max;

    public NumberRangeValidator(double min, double max) {
        this.min = min;
        this.max = max;
    }

    @Override
    public ValidationResult validate(Object value, String fieldName, int rowIndex) {
        if (value == null || value.toString().trim().isEmpty()) {
            return ValidationResult.success();
        }

        try {
            double number = Double.parseDouble(value.toString());
            if (number < min || number > max) {
                return ValidationResult.fail(
                    String.format("第%d行,%s数值超出范围:必须在 %.2f 到 %.2f 之间", 
                                 rowIndex + 1, fieldName, min, max)
                );
            }
        } catch (NumberFormatException e) {
            return ValidationResult.fail(
                String.format("第%d行,%s格式错误:必须是数字", rowIndex + 1, fieldName)
            );
        }

        return ValidationResult.success();
    }
}

/**
 * 自定义正则验证器
 */
public class RegexValidator implements ExcelValidator {

    private Pattern pattern;
    private String errorMessage;

    public RegexValidator(String regex, String errorMessage) {
        this.pattern = Pattern.compile(regex);
        this.errorMessage = errorMessage;
    }

    @Override
    public ValidationResult validate(Object value, String fieldName, int rowIndex) {
        if (value == null || value.toString().trim().isEmpty()) {
            return ValidationResult.success();
        }

        if (!pattern.matcher(value.toString()).matches()) {
            return ValidationResult.fail(
                String.format("第%d行,%s格式错误:%s", rowIndex + 1, fieldName, errorMessage)
            );
        }

        return ValidationResult.success();
    }
}

3.3 样式管理

java 复制代码
package com.example.excel.style;

import org.apache.poi.ss.usermodel.*;

/**
 * Excel样式管理器
 */
public class ExcelStyleManager {

    private Workbook workbook;

    public ExcelStyleManager(Workbook workbook) {
        this.workbook = workbook;
    }

    /**
     * 获取表头样式
     */
    public CellStyle getHeaderStyle() {
        CellStyle style = workbook.createCellStyle();

        // 字体
        Font font = workbook.createFont();
        font.setFontName("微软雅黑");
        font.setFontHeightInPoints((short) 11);
        font.setBold(true);
        font.setColor(IndexedColors.WHITE.getIndex());
        style.setFont(font);

        // 背景色
        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);

        // 边框
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);

        // 对齐方式
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);

        // 自动换行
        style.setWrapText(true);

        return style;
    }

    /**
     * 获取数据样式
     */
    public CellStyle getDataStyle() {
        CellStyle style = workbook.createCellStyle();

        // 字体
        Font font = workbook.createFont();
        font.setFontName("微软雅黑");
        font.setFontHeightInPoints((short) 10);
        style.setFont(font);

        // 边框
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);

        // 对齐方式
        style.setAlignment(HorizontalAlignment.LEFT);
        style.setVerticalAlignment(VerticalAlignment.CENTER);

        return style;
    }

    /**
     * 获取数字样式
     */
    public CellStyle getNumberStyle() {
        CellStyle style = getDataStyle();
        style.setAlignment(HorizontalAlignment.RIGHT);
        DataFormat format = workbook.createDataFormat();
        style.setDataFormat(format.getFormat("#,##0.00"));
        return style;
    }

    /**
     * 获取日期样式
     */
    public CellStyle getDateStyle() {
        CellStyle style = getDataStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        DataFormat format = workbook.createDataFormat();
        style.setDataFormat(format.getFormat("yyyy-mm-dd"));
        return style;
    }

    /**
     * 获取错误样式
     */
    public CellStyle getErrorStyle() {
        CellStyle style = getDataStyle();

        // 字体
        Font font = workbook.createFont();
        font.setColor(IndexedColors.RED.getIndex());
        style.setFont(font);

        return style;
    }

    /**
     * 创建自定义样式
     */
    public CellStyle createCustomStyle(StyleConfig config) {
        CellStyle style = workbook.createCellStyle();

        // 字体
        if (config.getFontName() != null) {
            Font font = workbook.createFont();
            font.setFontName(config.getFontName());
            font.setFontHeightInPoints(config.getFontSize());
            font.setBold(config.isBold());
            if (config.getFontColor() != null) {
                font.setColor(config.getFontColor());
            }
            style.setFont(font);
        }

        // 背景色
        if (config.getBackgroundColor() != null) {
            style.setFillForegroundColor(config.getBackgroundColor());
            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        }

        // 边框
        if (config.getBorderType() != null) {
            style.setBorderTop(config.getBorderType());
            style.setBorderBottom(config.getBorderType());
            style.setBorderLeft(config.getBorderType());
            style.setBorderRight(config.getBorderType());
        }

        // 对齐方式
        if (config.getHorizontalAlignment() != null) {
            style.setAlignment(config.getHorizontalAlignment());
        }
        if (config.getVerticalAlignment() != null) {
            style.setVerticalAlignment(config.getVerticalAlignment());
        }

        // 自动换行
        style.setWrapText(config.isWrapText());

        return style;
    }

    /**
     * 样式配置
     */
    public static class StyleConfig {
        private String fontName;
        private short fontSize = 10;
        private boolean bold;
        private Short fontColor;
        private Short backgroundColor;
        private BorderStyle borderType;
        private HorizontalAlignment horizontalAlignment;
        private VerticalAlignment verticalAlignment;
        private boolean wrapText;

        // Builder模式
        public static StyleConfig builder() {
            return new StyleConfig();
        }

        public StyleConfig fontName(String fontName) {
            this.fontName = fontName;
            return this;
        }

        public StyleConfig fontSize(short fontSize) {
            this.fontSize = fontSize;
            return this;
        }

        public StyleConfig bold(boolean bold) {
            this.bold = bold;
            return this;
        }

        public StyleConfig fontColor(Short fontColor) {
            this.fontColor = fontColor;
            return this;
        }

        public StyleConfig backgroundColor(Short backgroundColor) {
            this.backgroundColor = backgroundColor;
            return this;
        }

        public StyleConfig borderType(BorderStyle borderType) {
            this.borderType = borderType;
            return this;
        }

        public StyleConfig horizontalAlignment(HorizontalAlignment horizontalAlignment) {
            this.horizontalAlignment = horizontalAlignment;
            return this;
        }

        public StyleConfig verticalAlignment(VerticalAlignment verticalAlignment) {
            this.verticalAlignment = verticalAlignment;
            return this;
        }

        public StyleConfig wrapText(boolean wrapText) {
            this.wrapText = wrapText;
            return this;
        }

        // Getters
        public String getFontName() { return fontName; }
        public short getFontSize() { return fontSize; }
        public boolean isBold() { return bold; }
        public Short getFontColor() { return fontColor; }
        public Short getBackgroundColor() { return backgroundColor; }
        public BorderStyle getBorderType() { return borderType; }
        public HorizontalAlignment getHorizontalAlignment() { return horizontalAlignment; }
        public VerticalAlignment getVerticalAlignment() { return verticalAlignment; }
        public boolean isWrapText() { return wrapText; }
    }
}

四、核心工具类实现

4.1 Excel导出工具类

java 复制代码
package com.example.excel.util;

import com.example.excel.annotation.ExcelField;
import com.example.excel.annotation.ExcelIgnore;
import com.example.excel.annotation.ExcelSheet;
import com.example.excel.converter.ExcelConverter;
import com.example.excel.style.ExcelStyleManager;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * Excel导出工具类
 */
public class ExcelExportUtil {

    /**
     * 导出Excel
     * @param dataList 数据列表
     * @param outputStream 输出流
     * @param <T> 数据类型
     */
    public static <T> void export(List<T> dataList, OutputStream outputStream) {
        if (dataList == null || dataList.isEmpty()) {
            throw new IllegalArgumentException("数据列表不能为空");
        }

        Class<?> clazz = dataList.get(0).getClass();
        ExcelSheet sheetAnnotation = clazz.getAnnotation(ExcelSheet.class);

        // 创建工作簿
        try (SXSSFWorkbook workbook = new SXSSFWorkbook()) {
            // 创建工作表
            String sheetName = sheetAnnotation != null ? sheetAnnotation.name() : "Sheet1";
            Sheet sheet = workbook.createSheet(sheetName);

            // 样式管理器
            ExcelStyleManager styleManager = new ExcelStyleManager(workbook);

            // 获取字段信息
            List<FieldInfo> fieldInfos = getFieldInfos(clazz);

            // 创建表头
            if (sheetAnnotation == null || sheetAnnotation.createHeader()) {
                createHeader(sheet, fieldInfos, styleManager, sheetAnnotation);
            }

            // 填充数据
            fillData(sheet, dataList, fieldInfos, styleManager, sheetAnnotation);

            // 自动调整列宽
            if (sheetAnnotation == null || sheetAnnotation.autoSizeColumn()) {
                autoSizeColumn(sheet, fieldInfos.size());
            }

            // 写入输出流
            workbook.write(outputStream);

        } catch (Exception e) {
            throw new RuntimeException("导出Excel失败", e);
        }
    }

    /**
     * 获取字段信息
     */
    private static List<FieldInfo> getFieldInfos(Class<?> clazz) {
        List<FieldInfo> fieldInfos = new ArrayList<>();

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 跳过忽略的字段
            if (field.isAnnotationPresent(ExcelIgnore.class)) {
                continue;
            }

            ExcelField fieldAnnotation = field.getAnnotation(ExcelField.class);
            if (fieldAnnotation == null || !fieldAnnotation.export()) {
                continue;
            }

            field.setAccessible(true);
            fieldInfos.add(new FieldInfo(field, fieldAnnotation));
        }

        // 按index排序,未设置index的按字段顺序
        fieldInfos.sort((a, b) -> {
            int indexA = a.annotation.index();
            int indexB = b.annotation.index();
            if (indexA >= 0 && indexB >= 0) {
                return Integer.compare(indexA, indexB);
            } else if (indexA >= 0) {
                return -1;
            } else if (indexB >= 0) {
                return 1;
            }
            return 0;
        });

        return fieldInfos;
    }

    /**
     * 创建表头
     */
    private static void createHeader(Sheet sheet, List<FieldInfo> fieldInfos, 
                                    ExcelStyleManager styleManager, ExcelSheet sheetAnnotation) {
        CellStyle headerStyle = styleManager.getHeaderStyle();

        // 计算最大层级
        int maxLevel = 1;
        for (FieldInfo fieldInfo : fieldInfos) {
            String name = fieldInfo.annotation.name();
            int level = name.split("\\|").length;
            if (level > maxLevel) {
                maxLevel = level;
            }
        }

        // 创建多级表头
        for (int level = 0; level < maxLevel; level++) {
            Row row = sheet.createRow(level);

            int colIndex = 0;
            for (FieldInfo fieldInfo : fieldInfos) {
                String[] levels = fieldInfo.annotation.name().split("\\|");
                if (level < levels.length) {
                    Cell cell = row.createCell(colIndex);
                    cell.setCellValue(levels[level]);
                    cell.setCellStyle(headerStyle);

                    // 检查是否需要合并单元格
                    int rowspan = maxLevel - level - (levels.length - level - 1);
                    if (rowspan > 1) {
                        sheet.addMergedRegion(new CellRangeAddress(
                            level, level + rowspan - 1, colIndex, colIndex
                        ));
                    }

                    // 检查是否有子级需要横向合并
                    if (level < levels.length - 1) {
                        // 计算该层级有多少个子列
                        int childCount = countChildren(fieldInfos, colIndex, level, levels.length);
                        if (childCount > 1) {
                            sheet.addMergedRegion(new CellRangeAddress(
                                level, level, colIndex, colIndex + childCount - 1
                            ));
                        }
                    }

                    // 设置列宽
                    sheet.setColumnWidth(colIndex, fieldInfo.annotation.width() * 256);

                    colIndex++;
                }
            }
        }
    }

    /**
     * 计算子列数量
     */
    private static int countChildren(List<FieldInfo> fieldInfos, int startIndex, 
                                    int level, int totalLevels) {
        int count = 0;
        for (int i = startIndex; i < fieldInfos.size(); i++) {
            FieldInfo fieldInfo = fieldInfos.get(i);
            String[] levels = fieldInfo.annotation.name().split("\\|");
            if (levels.length > level && levels[level].equals(
                fieldInfos.get(startIndex).annotation.name().split("\\|")[level])) {
                count++;
            } else {
                break;
            }
        }
        return count;
    }

    /**
     * 填充数据
     */
    private static <T> void fillData(Sheet sheet, List<T> dataList, List<FieldInfo> fieldInfos,
                                    ExcelStyleManager styleManager, ExcelSheet sheetAnnotation) {
        int startRow = sheetAnnotation != null ? sheetAnnotation.titleRows() : 1;

        for (int i = 0; i < dataList.size(); i++) {
            T data = dataList.get(i);
            Row row = sheet.createRow(startRow + i);

            for (int j = 0; j < fieldInfos.size(); j++) {
                FieldInfo fieldInfo = fieldInfos.get(j);
                Cell cell = row.createCell(j);

                try {
                    Object value = fieldInfo.field.get(data);

                    // 应用转换器
                    if (fieldInfo.converter != null) {
                        value = fieldInfo.converter.exportValue(value);
                    }

                    // 设置单元格值
                    setCellValue(cell, value, fieldInfo.annotation.dataType(), styleManager);

                    // 设置样式
                    setCellStyle(cell, fieldInfo.annotation.dataType(), styleManager);

                } catch (IllegalAccessException e) {
                    cell.setCellValue("获取数据失败");
                }
            }
        }
    }

    /**
     * 设置单元格值
     */
    private static void setCellValue(Cell cell, Object value, ExcelField.DataType dataType,
                                     ExcelStyleManager styleManager) {
        if (value == null) {
            return;
        }

        switch (dataType) {
            case STRING:
                cell.setCellValue(value.toString());
                break;
            case NUMBER:
                if (value instanceof Number) {
                    cell.setCellValue(((Number) value).doubleValue());
                } else {
                    cell.setCellValue(Double.parseDouble(value.toString()));
                }
                break;
            case DATE:
                if (value instanceof java.util.Date) {
                    cell.setCellValue((java.util.Date) value);
                } else {
                    cell.setCellValue(value.toString());
                }
                break;
            case BOOLEAN:
                if (value instanceof Boolean) {
                    cell.setCellValue((Boolean) value);
                } else {
                    cell.setCellValue(Boolean.parseBoolean(value.toString()));
                }
                break;
            case FORMULA:
                cell.setCellFormula(value.toString());
                break;
            default:
                cell.setCellValue(value.toString());
        }
    }

    /**
     * 设置单元格样式
     */
    private static void setCellStyle(Cell cell, ExcelField.DataType dataType,
                                    ExcelStyleManager styleManager) {
        CellStyle style;
        switch (dataType) {
            case NUMBER:
                style = styleManager.getNumberStyle();
                break;
            case DATE:
                style = styleManager.getDateStyle();
                break;
            default:
                style = styleManager.getDataStyle();
        }
        cell.setCellStyle(style);
    }

    /**
     * 自动调整列宽
     */
    private static void autoSizeColumn(Sheet sheet, int columnCount) {
        for (int i = 0; i < columnCount; i++) {
            sheet.autoSizeColumn(i);
            // 设置最大宽度,防止列过宽
            int maxWidth = sheet.getColumnWidth(i);
            if (maxWidth > 10000) {
                sheet.setColumnWidth(i, 10000);
            }
        }
    }

    /**
     * 字段信息内部类
     */
    private static class FieldInfo {
        Field field;
        ExcelField annotation;
        ExcelConverter converter;

        FieldInfo(Field field, ExcelField annotation) {
            this.field = field;
            this.annotation = annotation;

            // 初始化转换器
            if (annotation.converter() != ExcelConverter.class) {
                try {
                    this.converter = annotation.converter().newInstance();
                } catch (Exception e) {
                    throw new RuntimeException("初始化转换器失败", e);
                }
            }
        }
    }
}

4.2 Excel导入工具类

java 复制代码
package com.example.excel.util;

import com.example.excel.annotation.ExcelField;
import com.example.excel.annotation.ExcelIgnore;
import com.example.excel.annotation.ExcelSheet;
import com.example.excel.converter.ExcelConverter;
import com.example.excel.validator.ExcelValidator;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * Excel导入工具类
 */
public class ExcelImportUtil {

    /**
     * 导入Excel
     * @param inputStream 输入流
     * @param clazz 目标类
     * @param <T> 数据类型
     * @return 导入结果
     */
    public static <T> ImportResult<T> importExcel(InputStream inputStream, Class<T> clazz) {
        List<T> dataList = new ArrayList<>();
        List<ImportError> errors = new ArrayList<>();

        try (Workbook workbook = new XSSFWorkbook(inputStream)) {
            Sheet sheet = workbook.getSheetAt(0);

            ExcelSheet sheetAnnotation = clazz.getAnnotation(ExcelSheet.class);
            int startRow = sheetAnnotation != null ? sheetAnnotation.titleRows() : 1;
            int totalRows = sheet.getLastRowNum();

            // 获取字段信息
            List<FieldInfo> fieldInfos = getFieldInfos(clazz);

            // 读取数据
            for (int i = startRow; i <= totalRows; i++) {
                Row row = sheet.getRow(i);
                if (row == null) {
                    continue;
                }

                try {
                    T instance = clazz.newInstance();

                    // 填充字段值
                    for (FieldInfo fieldInfo : fieldInfos) {
                        int colIndex = getFieldColumnIndex(fieldInfos, fieldInfo);
                        Cell cell = row.getCell(colIndex);

                        if (cell != null) {
                            Object value = getCellValue(cell);

                            // 应用转换器
                            if (fieldInfo.converter != null) {
                                value = fieldInfo.converter.importValue(value);
                            }

                            // 验证数据
                            ValidationResult validationResult = validateData(
                                value, fieldInfo, i, fieldInfo.annotation
                            );

                            if (!validationResult.isValid()) {
                                errors.add(new ImportError(
                                    i + 1, 
                                    fieldInfo.annotation.name(), 
                                    validationResult.getMessage()
                                ));
                                continue;
                            }

                            // 设置字段值
                            setFieldValue(instance, fieldInfo.field, value);
                        } else if (fieldInfo.annotation.required()) {
                            errors.add(new ImportError(
                                i + 1,
                                fieldInfo.annotation.name(),
                                fieldInfo.annotation.requiredMessage()
                            ));
                        }
                    }

                    dataList.add(instance);

                } catch (Exception e) {
                    errors.add(new ImportError(
                        i + 1,
                        "整行",
                        "解析失败:" + e.getMessage()
                    ));
                }
            }

            return new ImportResult<>(dataList, errors);

        } catch (Exception e) {
            throw new RuntimeException("导入Excel失败", e);
        }
    }

    /**
     * 获取字段信息
     */
    private static List<FieldInfo> getFieldInfos(Class<?> clazz) {
        List<FieldInfo> fieldInfos = new ArrayList<>();

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(ExcelIgnore.class)) {
                continue;
            }

            ExcelField fieldAnnotation = field.getAnnotation(ExcelField.class);
            if (fieldAnnotation == null || !fieldAnnotation.import()) {
                continue;
            }

            field.setAccessible(true);
            fieldInfos.add(new FieldInfo(field, fieldAnnotation));
        }

        // 按index排序
        fieldInfos.sort((a, b) -> {
            int indexA = a.annotation.index();
            int indexB = b.annotation.index();
            if (indexA >= 0 && indexB >= 0) {
                return Integer.compare(indexA, indexB);
            } else if (indexA >= 0) {
                return -1;
            } else if (indexB >= 0) {
                return 1;
            }
            return 0;
        });

        return fieldInfos;
    }

    /**
     * 获取字段列索引
     */
    private static int getFieldColumnIndex(List<FieldInfo> fieldInfos, FieldInfo target) {
        for (int i = 0; i < fieldInfos.size(); i++) {
            if (fieldInfos.get(i) == target) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 获取单元格值
     */
    private static Object getCellValue(Cell cell) {
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue().trim();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue();
                }
                return cell.getNumericCellValue();
            case BOOLEAN:
                return cell.getBooleanCellValue();
            case FORMULA:
                return cell.getCellFormula();
            case BLANK:
                return null;
            default:
                return null;
        }
    }

    /**
     * 验证数据
     */
    private static ValidationResult validateData(Object value, FieldInfo fieldInfo, 
                                                 int rowIndex, ExcelField annotation) {
        // 必填验证
        if (annotation.required() && (value == null || value.toString().trim().isEmpty())) {
            return ValidationResult.fail(annotation.requiredMessage());
        }

        // 应用验证器
        if (fieldInfo.validator != null) {
            return fieldInfo.validator.validate(value, annotation.name(), rowIndex);
        }

        return ValidationResult.success();
    }

    /**
     * 设置字段值
     */
    private static void setFieldValue(Object instance, Field field, Object value) 
            throws IllegalAccessException {
        if (value == null) {
            return;
        }

        Class<?> fieldType = field.getType();

        // 类型转换
        if (fieldType == String.class) {
            field.set(instance, value.toString());
        } else if (fieldType == Integer.class || fieldType == int.class) {
            if (value instanceof Number) {
                field.set(instance, ((Number) value).intValue());
            } else {
                field.set(instance, Integer.parseInt(value.toString()));
            }
        } else if (fieldType == Long.class || fieldType == long.class) {
            if (value instanceof Number) {
                field.set(instance, ((Number) value).longValue());
            } else {
                field.set(instance, Long.parseLong(value.toString()));
            }
        } else if (fieldType == Double.class || fieldType == double.class) {
            if (value instanceof Number) {
                field.set(instance, ((Number) value).doubleValue());
            } else {
                field.set(instance, Double.parseDouble(value.toString()));
            }
        } else if (fieldType == Boolean.class || fieldType == boolean.class) {
            if (value instanceof Boolean) {
                field.set(instance, value);
            } else {
                field.set(instance, Boolean.parseBoolean(value.toString()));
            }
        } else if (fieldType == java.util.Date.class) {
            if (value instanceof java.util.Date) {
                field.set(instance, value);
            } else {
                // 需要根据format转换
                throw new RuntimeException("日期类型需要配置转换器");
            }
        } else {
            field.set(instance, value);
        }
    }

    /**
     * 字段信息内部类
     */
    private static class FieldInfo {
        Field field;
        ExcelField annotation;
        ExcelConverter converter;
        ExcelValidator validator;

        FieldInfo(Field field, ExcelField annotation) {
            this.field = field;
            this.annotation = annotation;

            // 初始化转换器
            if (annotation.converter() != ExcelConverter.class) {
                try {
                    this.converter = annotation.converter().newInstance();
                } catch (Exception e) {
                    throw new RuntimeException("初始化转换器失败", e);
                }
            }

            // 初始化验证器
            if (annotation.validator() != ExcelValidator.class) {
                try {
                    this.validator = annotation.validator().newInstance();
                } catch (Exception e) {
                    throw new RuntimeException("初始化验证器失败", e);
                }
            }
        }
    }

    /**
     * 导入结果
     */
    public static class ImportResult<T> {
        private List<T> dataList;
        private List<ImportError> errors;

        public ImportResult(List<T> dataList, List<ImportError> errors) {
            this.dataList = dataList;
            this.errors = errors;
        }

        public List<T> getDataList() {
            return dataList;
        }

        public List<ImportError> getErrors() {
            return errors;
        }

        public boolean hasErrors() {
            return !errors.isEmpty();
        }
    }

    /**
     * 导入错误
     */
    public static class ImportError {
        private int rowIndex;
        private String fieldName;
        private String message;

        public ImportError(int rowIndex, String fieldName, String message) {
            this.rowIndex = rowIndex;
            this.fieldName = fieldName;
            this.message = message;
        }

        public int getRowIndex() {
            return rowIndex;
        }

        public String getFieldName() {
            return fieldName;
        }

        public String getMessage() {
            return message;
        }

        @Override
        public String toString() {
            return String.format("第%d行,%s:%s", rowIndex, fieldName, message);
        }
    }

    /**
     * 验证结果
     */
    private static class ValidationResult {
        private boolean valid;
        private String message;

        public ValidationResult(boolean valid, String message) {
            this.valid = valid;
            this.message = message;
        }

        public static ValidationResult success() {
            return new ValidationResult(true, null);
        }

        public static ValidationResult fail(String message) {
            return new ValidationResult(false, message);
        }

        public boolean isValid() {
            return valid;
        }

        public String getMessage() {
            return message;
        }
    }
}

五、使用示例

5.1 定义实体类

java 复制代码
package com.example.entity;

import com.example.excel.annotation.*;
import com.example.excel.converter.impl.*;
import com.example.excel.validator.impl.*;
import lombok.Data;
import java.util.Date;

/**
 * 用户实体
 */
@Data
@ExcelSheet(name = "用户信息", titleRows = 2, autoSizeColumn = true)
public class UserExcel {

    @ExcelField(name = "基本信息|工号", index = 0, width = 15)
    private String employeeNo;

    @ExcelField(
        name = "基本信息|姓名", 
        index = 1, 
        width = 12,
        required = true,
        requiredMessage = "姓名不能为空"
    )
    private String name;

    @ExcelField(
        name = "基本信息|性别", 
        index = 2, 
        width = 8,
        options = {"男", "女"}
    )
    private String gender;

    @ExcelField(
        name = "基本信息|年龄", 
        index = 3, 
        width = 8,
        dataType = ExcelField.DataType.NUMBER
    )
    private Integer age;

    @ExcelField(
        name = "联系方式|手机号", 
        index = 4, 
        width = 15,
        validator = PhoneValidator.class
    )
    private String phone;

    @ExcelField(
        name = "联系方式|邮箱", 
        index = 5, 
        width = 20,
        validator = EmailValidator.class
    )
    private String email;

    @ExcelField(
        name = "工作信息|部门", 
        index = 6, 
        width = 15
    )
    private String department;

    @ExcelField(
        name = "工作信息|职位", 
        index = 7, 
        width = 15
    )
    private String position;

    @ExcelField(
        name = "工作信息|入职日期", 
        index = 8, 
        width = 15,
        dataType = ExcelField.DataType.DATE,
        converter = DateConverter.class,
        format = "yyyy-MM-dd"
    )
    private Date entryDate;

    @ExcelField(
        name = "工作信息|薪资", 
        index = 9, 
        width = 12,
        dataType = ExcelField.DataType.NUMBER,
        converter = DateConverter.class
    )
    private Double salary;

    @ExcelField(
        name = "其他|状态", 
        index = 10, 
        width = 10,
        converter = StatusConverter.class
    )
    private UserStatus status;

    @ExcelField(
        name = "其他|备注", 
        index = 11, 
        width = 30
    )
    private String remark;

    // 忽略导入导出的字段
    @ExcelIgnore
    private String password;

    /**
     * 用户状态枚举
     */
    public enum UserStatus {
        ACTIVE("在职"),
        INACTIVE("离职"),
        SUSPENDED("停职");

        private String desc;

        UserStatus(String desc) {
            this.desc = desc;
        }

        public String getDesc() {
            return desc;
        }
    }

    /**
     * 状态转换器
     */
    public static class StatusConverter implements ExcelConverter {
        @Override
        public Object exportValue(Object value) {
            if (value == null) {
                return null;
            }
            return ((UserStatus) value).getDesc();
        }

        @Override
        public Object importValue(Object value) {
            if (value == null) {
                return null;
            }
            String desc = value.toString();
            for (UserStatus status : UserStatus.values()) {
                if (status.getDesc().equals(desc)) {
                    return status;
                }
            }
            throw new RuntimeException("无效的状态值:" + desc);
        }
    }
}

5.2 导出示例

java 复制代码
package com.example.controller;

import com.example.entity.UserExcel;
import com.example.excel.util.ExcelExportUtil;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

@RestController
@RequestMapping("/api/excel")
public class ExcelExportController {

    /**
     * 导出用户数据
     */
    @GetMapping("/export/users")
    public void exportUsers(HttpServletResponse response) throws IOException {
        // 准备数据
        List<UserExcel> userList = new ArrayList<>();

        UserExcel user1 = new UserExcel();
        user1.setEmployeeNo("E001");
        user1.setName("张三");
        user1.setGender("男");
        user1.setAge(28);
        user1.setPhone("13800138000");
        user1.setEmail("zhangsan@example.com");
        user1.setDepartment("技术部");
        user1.setPosition("Java开发工程师");
        user1.setEntryDate(new Date());
        user1.setSalary(15000.0);
        user1.setStatus(UserExcel.UserStatus.ACTIVE);
        user1.setRemark("优秀员工");
        userList.add(user1);

        UserExcel user2 = new UserExcel();
        user2.setEmployeeNo("E002");
        user2.setName("李四");
        user2.setGender("女");
        user2.setAge(26);
        user2.setPhone("13900139000");
        user2.setEmail("lisi@example.com");
        user2.setDepartment("产品部");
        user2.setPosition("产品经理");
        user2.setEntryDate(new Date());
        user2.setSalary(18000.0);
        user2.setStatus(UserExcel.UserStatus.ACTIVE);
        user2.setRemark("工作认真");
        userList.add(user2);

        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String fileName = "用户信息_" + System.currentTimeMillis() + ".xlsx";
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);

        // 导出Excel
        ExcelExportUtil.export(userList, response.getOutputStream());
    }
}

5.3 导入示例

java 复制代码
package com.example.controller;

import com.example.entity.UserExcel;
import com.example.excel.util.ExcelImportUtil;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/excel")
public class ExcelImportController {

    /**
     * 导入用户数据
     */
    @PostMapping("/import/users")
    public Map<String, Object> importUsers(@RequestParam("file") MultipartFile file) {
        Map<String, Object> result = new HashMap<>();

        try {
            // 导入Excel
            ExcelImportUtil.ImportResult<UserExcel> importResult = 
                ExcelImportUtil.importExcel(file.getInputStream(), UserExcel.class);

            // 检查是否有错误
            if (importResult.hasErrors()) {
                result.put("success", false);
                result.put("message", "导入失败,存在错误数据");
                result.put("errors", importResult.getErrors());
                return result;
            }

            // 保存数据
            List<UserExcel> userList = importResult.getDataList();
            userService.batchSave(userList);

            result.put("success", true);
            result.put("message", "导入成功,共导入 " + userList.size() + " 条数据");
            result.put("data", userList);

        } catch (IOException e) {
            result.put("success", false);
            result.put("message", "文件读取失败:" + e.getMessage());
        } catch (Exception e) {
            result.put("success", false);
            result.put("message", "导入失败:" + e.getMessage());
        }

        return result;
    }
}

六、高级功能

6.1 动态列配置

java 复制代码
package com.example.excel.config;

import com.example.excel.annotation.ExcelField;
import lombok.Data;

/**
 * 动态列配置
 */
@Data
public class DynamicColumnConfig {

    /**
     * 列名
     */
    private String name;

    /**
     * 字段名
     */
    private String fieldName;

    /**
     * 列宽
     */
    private int width = 15;

    /**
     * 数据类型
     */
    private ExcelField.DataType dataType = ExcelField.DataType.STRING;

    /**
     * 格式
     */
    private String format;

    /**
     * 是否显示
     */
    private boolean visible = true;

    /**
     * 排序
     */
    private int order;
}

6.2 模板导出

java 复制代码
package com.example.excel.util;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

/**
 * Excel模板导出工具
 */
public class ExcelTemplateUtil {

    /**
     * 基于模板导出
     * @param templateStream 模板文件流
     * @param dataMap 数据映射
     * @param outputStream 输出流
     */
    public static void exportByTemplate(InputStream templateStream, 
                                       Map<String, Object> dataMap,
                                       OutputStream outputStream) {
        try (Workbook workbook = new XSSFWorkbook(templateStream)) {
            Sheet sheet = workbook.getSheetAt(0);

            // 替换模板中的占位符
            replacePlaceholders(sheet, dataMap);

            // 写入输出流
            workbook.write(outputStream);

        } catch (Exception e) {
            throw new RuntimeException("模板导出失败", e);
        }
    }

    /**
     * 替换占位符
     */
    private static void replacePlaceholders(Sheet sheet, Map<String, Object> dataMap) {
        for (Row row : sheet) {
            for (Cell cell : row) {
                if (cell.getCellType() == CellType.STRING) {
                    String value = cell.getStringCellValue();
                    if (value != null && value.startsWith("${") && value.endsWith("}")) {
                        String key = value.substring(2, value.length() - 1);
                        Object data = dataMap.get(key);
                        if (data != null) {
                            cell.setCellValue(data.toString());
                        }
                    }
                }
            }
        }
    }
}

6.3 数据验证配置

java 复制代码
package com.example.excel.validation;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;

/**
 * Excel数据验证工具
 */
public class ExcelDataValidationUtil {

    /**
     * 添加下拉验证
     * @param sheet 工作表
     * @param firstRow 起始行
     * @param lastRow 结束行
     * @param firstCol 起始列
     * @param lastCol 结束列
     * @param options 选项列表
     */
    public static void addDropdownValidation(Sheet sheet, int firstRow, int lastRow,
                                            int firstCol, int lastCol, String[] options) {
        DataValidationHelper validationHelper = sheet.getDataValidationHelper();

        // 创建下拉列表
        DataValidationConstraint constraint = validationHelper.createExplicitListConstraint(options);

        // 设置验证区域
        CellRangeAddressList addressList = new CellRangeAddressList(
            firstRow, lastRow, firstCol, lastCol
        );

        // 创建数据验证
        DataValidation validation = validationHelper.createValidation(constraint, addressList);

        // 设置错误提示
        validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
        validation.createErrorBox("输入错误", "请从下拉列表中选择");

        // 添加到工作表
        sheet.addValidationData(validation);
    }

    /**
     * 添加数字范围验证
     * @param sheet 工作表
     * @param firstRow 起始行
     * @param lastRow 结束行
     * @param firstCol 起始列
     * @param lastCol 结束列
     * @param min 最小值
     * @param max 最大值
     */
    public static void addNumberRangeValidation(Sheet sheet, int firstRow, int lastRow,
                                               int firstCol, int lastCol, double min, double max) {
        DataValidationHelper validationHelper = sheet.getDataValidationHelper();

        // 创建数字约束
        DataValidationConstraint constraint = validationHelper.createNumericConstraint(
            DataValidationConstraint.ValidationType.DECIMAL,
            DataValidationConstraint.OperatorType.BETWEEN,
            String.valueOf(min),
            String.valueOf(max)
        );

        // 设置验证区域
        CellRangeAddressList addressList = new CellRangeAddressList(
            firstRow, lastRow, firstCol, lastCol
        );

        // 创建数据验证
        DataValidation validation = validationHelper.createValidation(constraint, addressList);

        // 设置错误提示
        validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
        validation.createErrorBox("输入错误", 
            String.format("请输入 %.2f 到 %.2f 之间的数字", min, max));

        // 添加到工作表
        sheet.addValidationData(validation);
    }
}

七、性能优化

7.1 大数据量处理

java 复制代码
package com.example.excel.util;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.io.OutputStream;
import java.util.List;

/**
 * 大数据量Excel导出工具
 */
public class BigDataExcelExportUtil {

    /**
     * 分批导出大数据量
     * @param dataProvider 数据提供者
     * @param outputStream 输出流
     * @param batchSize 批次大小
     */
    public static <T> void exportBatch(DataProvider<T> dataProvider, 
                                      OutputStream outputStream, 
                                      int batchSize) {
        try (SXSSFWorkbook workbook = new SXSSFWorkbook(1000)) { // 保留1000行在内存中
            Sheet sheet = workbook.createSheet("数据");

            int rowNum = 0;
            List<T> batch;

            while ((batch = dataProvider.getNextBatch(batchSize)) != null && !batch.isEmpty()) {
                for (T data : batch) {
                    Row row = sheet.createRow(rowNum++);
                    // 填充数据
                    fillRow(row, data);
                }

                // 定期刷新到磁盘
                if (rowNum % 1000 == 0) {
                    ((SXSSFSheet) sheet).flushRows();
                }
            }

            workbook.write(outputStream);

        } catch (Exception e) {
            throw new RuntimeException("大数据量导出失败", e);
        }
    }

    /**
     * 数据提供者接口
     */
    public interface DataProvider<T> {
        /**
         * 获取下一批数据
         */
        List<T> getNextBatch(int batchSize);
    }

    private static <T> void fillRow(Row row, T data) {
        // 实现数据填充逻辑
    }
}

7.2 并行处理

java 复制代码
package com.example.excel.util;

import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 并行Excel处理工具
 */
public class ParallelExcelUtil {

    private static final ExecutorService executor = 
        Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    /**
     * 并行处理数据
     * @param dataList 数据列表
     * @param processor 处理器
     * @return 处理结果列表
     */
    public static <T, R> List<R> processParallel(List<T> dataList, 
                                                Processor<T, R> processor) {
        List<CompletableFuture<R>> futures = dataList.stream()
            .map(data -> CompletableFuture.supplyAsync(
                () -> processor.process(data), 
                executor
            ))
            .collect(Collectors.toList());

        return futures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());
    }

    /**
     * 处理器接口
     */
    public interface Processor<T, R> {
        R process(T data);
    }

    /**
     * 关闭线程池
     */
    public static void shutdown() {
        executor.shutdown();
    }
}

八、最佳实践与注意事项

8.1 使用建议

  1. 注解优先:优先使用注解配置,减少代码侵入性
  2. 合理分批:大数据量导入导出时,使用分批处理
  3. 异常处理:完善的异常处理和错误提示
  4. 性能监控:监控导入导出性能,及时优化
  5. 资源管理:使用try-with-resources确保资源释放

8.2 常见问题

Q1:如何处理复杂的嵌套对象?

A:使用转换器在导入导出时进行对象转换,或者在实体类中增加扁平化字段。

Q2:如何处理动态列?

A:使用DynamicColumnConfig配置动态列,或者使用模板导出方式。

Q3:大数据量导出内存占用过高?

A:使用SXSSFWorkbook的流式处理,设置合适的内存缓存行数。

Q4:如何自定义样式?

A:通过ExcelStyleManager创建自定义样式,或者在注解中指定样式配置。

8.3 团队规范建议

  1. 统一注解规范:团队内统一使用相同的注解配置
  2. 命名规范:导出文件名包含业务和时间戳
  3. 数据验证:所有导入数据必须经过验证
  4. 日志记录:记录导入导出的关键操作和错误
  5. 版本管理:Excel模板版本化管理

九、总结

本文实现了一个功能完善的通用Excel导入导出工具类,核心功能:

  • ✅ 注解驱动,使用简单
  • ✅ 支持复杂多级表头
  • ✅ 支持动态列配置
  • ✅ 内置常用转换器和验证器
  • ✅ 支持自定义转换器和验证器
  • ✅ 完善的错误提示机制
  • ✅ 大数据量性能优化
  • ✅ 模板导出支持
  • ✅ 数据验证支持
相关推荐
独自破碎E2 小时前
【面试真题拆解】Java锁机制:synchronized、ReentrantLock、锁升级、可重入锁
java·开发语言·面试
努力往上爬de蜗牛2 小时前
extends
java·开发语言
逆境不可逃2 小时前
【从零入门23种设计模式23】行为型之模板模式
java·开发语言·算法·设计模式·职场和发展·模板模式
IronMurphy2 小时前
【算法二十五】105. 从前序与中序遍历序列构造二叉树 236. 二叉树的最近公共祖先
java·数据结构·算法
snakeshe10102 小时前
从 MySQL 到 Elasticsearch:构建高性能新闻爬虫的数据存储与搜索体系
java
技术小白菜2 小时前
海康平台通过代理播放视频流
java·java ee
学习3人组2 小时前
Workerman实现 WSS 基于客户端 ID 的精准推送
android·java·开发语言
百结2142 小时前
Nginx性能优化与监控实战
java·nginx·性能优化
jason_renyu2 小时前
Maven 新手完全使用指南(完整版)
java·maven·maven新手指南·maven新手完全使用指南·maven新手使用教程·maven教程