# excel-poi-util **Repository Path**: chongyahhh/excel-poi-util ## Basic Information - **Project Name**: excel-poi-util - **Description**: poi封装excel导出 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2021-01-06 - **Last Updated**: 2021-08-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # excel-poi-util #### 介绍 poi封装excel导出工具 # 前言   首先,当准备看这个博客的时候,就默认大家已经会使用spring进行javaweb开发以及使用poi导出excel。相信大家在进行excel导出的时候都会发现,代码冗余较多,如果每导出一个excel文件就写一段代码的话,代码重复量巨大,出了问题也难以定位问题的所在。那么自己封装一个导出excel的工具类就显得十分有意义了。   那么我们可以通过什么方式来构建这个工具类呢?   相信大家在使用spring开发项目的时候都使用过hibernate、mybatis来查询处理数据库中的数据,而这类框架的名称叫做ORM(Object Relational Mapping)框架,即对象关系映射框架。ORM的思想是将数据库中的每一个关系(即表)映射到一个实体类上,用类代表表、对象代表一条条记录、类的属性代表表中的字段。   大家也都知道,数据库通常分为关系型数据库和非关系型数据库,而关系型数据库中数据的存储展示方式与excel表格十分相似,那我们为什么不能模仿ORM来进行excel导出辅助工具类的设计呢? # 分析 ## excel组成   在生成excel之前我们首先要知道一个excel文件的结构以及一个excel文件通常是什么样的。 ### 文件组成   首先我们来看一个简单的例子,这是关于学生信息统计的excel表格。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200806173717481.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0NzUzNDUx,size_16,color_FFFFFF,t_70)   在图中我们可以发现一个excel文件结构是:一个excel文件(A对应的xxx.xls)包含多个sheet(C对应的部分),而一个sheet包含多个单元格(cell)(B对应的部分)。 ### sheet的通常使用格式   通常我们将一个sheet分成四个模块:标题、表头、数据和标尾。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200806174455510.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0NzUzNDUx,size_16,color_FFFFFF,t_70)   但是我们知道,一个excel表格中,只有表头和数据部分是必须出现的,少了任何一个便不能成为一个完整的数据呈现载体。而标题和标尾不能强求,说不定别人一个文件想要,另一个就不想要了,所以表头和数据部分是必须的,而标题和标尾应该可以供用户选择,留出扩展实现的接口。 # 工具类的设计与实现   我们使用自底向上的思路,从cell开始,直到excel文件。 ## 数据单元   我们知道,在spring-data-jpa中是使用@Entity来标明这个类这是一个实体,需要映射到某张表;而用@Column标明这个属性需要映射到表中的一个字段。那我们是不是也可以通过注解的方式来标注实体的映射信息呢?   我们可以分别用两个注解,一个类注解来表明这个实体类对应一个sheet,一个属性注解来表明这个属性对应一个表头。(**由于时间原因这里只实现了后者**)   话不多说,直接贴一下代码: ```java @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ExcelColumn { /** * 该字段出现在 excel 表格的顺序 * * @return */ int order(); /** * 该字段在表头的名称 * * @return */ String description(); /** * 标题字体大小 * * @return */ short titleFontSize() default (short) 10; /** * 标题字体粗细 * * @return */ short titleFontWeight() default HSSFFont.BOLDWEIGHT_NORMAL; /** * 标题字体颜色 * * @return */ short titleFontColor() default Font.COLOR_NORMAL; /** * 正文字体大小 * * @return */ short contentFontSize() default (short) 10; /** * 正文字体粗细 * * @return */ short contentFontWeight() default HSSFFont.BOLDWEIGHT_NORMAL; /** * 正文字体颜色 * * @return */ short contentFontColor() default Font.COLOR_NORMAL; /** * 是否对这一列上锁 * * @return */ boolean columnLocked() default true; } ```   这里除了字体格式、颜色、大小以及是否对该列上锁等基本信息,需要注意的两个属性是order和description,这两个属性不能为空,也是格式构造必不可少的两个部分:   order:order是这个属性对应的权重,越小,该属性对应的列就越靠前,就像最开始学生信息表,order:学号 < 姓名 < 性别 < 住址。   description:description是该属性对应的列的表头,如最开始学生信息表的学号、姓名、性别和住址。   使用方式如下: ```java class Student{ @ExcelColumn(order = 1, description = "学号") private String id; @ExcelColumn(order = 2, description = "姓名") private String name; @ExcelColumn(order = 4, description = "住址") private String address; @ExcelColumn(order = 3, description = "性别") private String sex; // 构造方法和getter、setter方法省略 } ``` ## sheet   现在单元格的载体已经设计好,可以设计单元格的载体sheet了,其实大家也都熟悉sheet,所以这里直接放上代码: ```java public class Sheet { private String sheetName; // sheet的名称 private List orderedTitles; // 排好序的每一列的表头 private List data; // 具体的数据 private Class targetClass; // 承载数据的类,如:Student private List orderedFields; // 排好序的属性 private List titleStyle; // 表头的格式 private List contentStyle; // 正文数据的格式 private HSSFWorkbook hssfWorkbook; // 所属的excel文件 private ExcelProcessor excelProcessor = new StandardExcelProcessor(); // 处理器(这里默认是标准处理器) private boolean customMode = false; // 是否为自定义模式 // 构造方法和getter、setter方法省略 } ```   这里将要利用反射机制对属性进行排序,具体代码如下: ```java private void initExcelAssistBean() { Field[] fields = targetClass.getDeclaredFields(); orderedFields = new ArrayList<>(); for (Field temp : fields) { if (temp.getAnnotation(ExcelColumn.class) != null) { orderedFields.add(temp); } } orderedFields.sort(Comparator.comparingInt(field -> field.getAnnotation(ExcelColumn.class).order())); orderedTitles = new ArrayList<>(); for (Field temp : orderedFields) { orderedTitles.add(temp.getAnnotation(ExcelColumn.class).description()); } //如果开启自定义模式 if (customMode) { titleStyle = new ArrayList<>(); contentStyle = new ArrayList<>(); HSSFCellStyle currentCellStyle; Font font; for (Field temp : orderedFields) { currentCellStyle = hssfWorkbook.createCellStyle(); font = hssfWorkbook.createFont(); font.setFontHeightInPoints(temp.getAnnotation(ExcelColumn.class).titleFontSize()); font.setBoldweight(temp.getAnnotation(ExcelColumn.class).titleFontWeight()); font.setColor(temp.getAnnotation(ExcelColumn.class).titleFontColor()); currentCellStyle.setLocked(false); // 表头均不能修改 currentCellStyle.setFont(font); titleStyle.add(currentCellStyle); currentCellStyle = hssfWorkbook.createCellStyle(); font = hssfWorkbook.createFont(); font.setFontHeightInPoints(temp.getAnnotation(ExcelColumn.class).contentFontSize()); font.setBoldweight(temp.getAnnotation(ExcelColumn.class).contentFontWeight()); font.setColor(temp.getAnnotation(ExcelColumn.class).contentFontColor()); currentCellStyle.setLocked(temp.getAnnotation(ExcelColumn.class).columnLocked()); currentCellStyle.setFont(font); contentStyle.add(currentCellStyle); } } } ```   该类的每个构造方法都会调用这个初始化方法,而这个方法主要是对属性进行排序(通过order),以及设置表头和正文各种格式样式。 ## excel文件载体   虽说可以专门抽象出一个类来描述一个excel文件,可转念一想,一个excel文件其实包含的属性比较少,所以就和工厂类抽象成同一个就好了,下面是代码: ```java public class ExcelWorkBookCreator { private String excelFileName; // 文件名称 private List sheets; // 文件包含的sheets private HSSFWorkbook hssfWorkbook; // 文件载体 private final String EXCEL_SHEET_PASSWORD = "123321"; // 文件密码 // 构造方法和getter、setter方法省略 } ``` ## 通用工具类   在这个类中可以获取ExcelWorkBookCreator,以及一些简单的通用操作: ```java public class ExcelUtil { /** * 获取 excel 文件创造器 * * @param excelFileName -> excel 文件的名字 * @return */ public static ExcelWorkBookCreator getExcelWorkBookCreator(String excelFileName) { return new ExcelWorkBookCreator(excelFileName); } } ``` ## 处理器   大家是否还记着之前说的:通常我们将一个sheet分成四个模块:标题、表头、数据和标尾。而标题和标尾不是必须不可的,而在上面我们也没有给这两个模块留有相应的余地。不光如此,一个封装工具无论怎样设计都难做到满足所有需求,所以我们必须留给自己足够的余地。这个时候就很需要处理器了。这里我们模仿spring中ioc容器的PostProcessor,在绘制表头和正文之前(自定义标题)和之后(自定义标尾以及处理一些特殊数据)让用户进行自定义的处理。 ```java public interface ExcelProcessor { /** * 在正式注入数据之前的自定义操作,可以自己设计标题等 * * @param sheet -> 当前 sheet 信息 * @param currentSheet -> 当前 sheet * @return 想要正式注入表头及数据的行数 */ Integer customOperationBeforeInvokeData(Sheet sheet, HSSFSheet currentSheet); /** * 在注入完数据后的自定义操作,如:在表格最后落上日期、对某些数据进行修改等 * 可以通过 currentSheet..getLastRowNum() 获取最后一行的行标,从 0 开始 * * @param sheet -> 当前 sheet 信息 * @param currentSheet -> 当前 sheet */ void customOperationAfterInvokeData(Sheet sheet, HSSFSheet currentSheet, int firstRowNum); } ```   通过实现这个接口,我们便可以进行一些扩展的操作。 # 功能分配汇总 ExcelWorkBookCreator(层级:文件):保存所有sheet,可以导出文件。 Sheet(层次:sheet):保存sheet信息,可以注入数据。 ExcelUtil:进行一些通用操作。 ExcelProcessor:在文件构造前后进行自定义处理。 # 需要的依赖 ```xml org.apache.poi poi 3.12 ``` # 总结   由于从想到要创建这个工具类到实现只用了一两天的时间,所以有许多设计其实并不是特别合理。而且这个工具类也没有使用过,所以可能**会出现很多bug**。但这里其实我只是提供一种思路,实现还是自己来的好,毕竟用自己实现的才安心。另外这些代码仅供学习,如果在实际环境中使用出现问题,请自负责任。 # 代码   **这里是所有内容的代码,仅供学习使用**! ## ExcelColumn ```java @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ExcelColumn { /** * 该字段出现在 excel 表格的顺序 * * @return */ int order(); /** * 该字段在表头的名称 * * @return */ String description(); /** * 标题字体大小 * * @return */ short titleFontSize() default (short) 10; /** * 标题字体粗细 * * @return */ short titleFontWeight() default HSSFFont.BOLDWEIGHT_NORMAL; /** * 标题字体颜色 * * @return */ short titleFontColor() default Font.COLOR_NORMAL; /** * 正文字体大小 * * @return */ short contentFontSize() default (short) 10; /** * 正文字体粗细 * * @return */ short contentFontWeight() default HSSFFont.BOLDWEIGHT_NORMAL; /** * 正文字体颜色 * * @return */ short contentFontColor() default Font.COLOR_NORMAL; /** * 是否对这一列上锁 * * @return */ boolean columnLocked() default true; } ``` ## Sheet ```java public class Sheet { private String sheetName; private List orderedTitles; private List data; private Class targetClass; private List orderedFields; private List titleStyle; private List contentStyle; private HSSFWorkbook hssfWorkbook; private ExcelProcessor excelProcessor = new StandardExcelProcessor(); private boolean customMode = false; public Sheet() { } public Sheet(HSSFWorkbook hssfWorkbook, String sheetName, List data, Class targetClass) { this.hssfWorkbook = hssfWorkbook; this.sheetName = sheetName; this.data = data; this.targetClass = targetClass; initExcelAssistBean(); } public Sheet(HSSFWorkbook hssfWorkbook, String sheetName, List data, Class targetClass, ExcelProcessor excelProcessor) { this.hssfWorkbook = hssfWorkbook; this.sheetName = sheetName; this.data = data; this.targetClass = targetClass; this.excelProcessor = excelProcessor; initExcelAssistBean(); } public Sheet(HSSFWorkbook hssfWorkbook, String sheetName, List data, Class targetClass, boolean customMode) { this.hssfWorkbook = hssfWorkbook; this.sheetName = sheetName; this.data = data; this.targetClass = targetClass; this.customMode = customMode; initExcelAssistBean(); } public Sheet(HSSFWorkbook hssfWorkbook, String sheetName, List data, Class targetClass, ExcelProcessor excelProcessor, boolean customMode) { this.hssfWorkbook = hssfWorkbook; this.sheetName = sheetName; this.data = data; this.targetClass = targetClass; this.excelProcessor = excelProcessor; this.customMode = customMode; initExcelAssistBean(); } private void initExcelAssistBean() { Field[] fields = targetClass.getDeclaredFields(); orderedFields = new ArrayList<>(); for (Field temp : fields) { if (temp.getAnnotation(ExcelColumn.class) != null) { orderedFields.add(temp); } } orderedFields.sort(Comparator.comparingInt(field -> field.getAnnotation(ExcelColumn.class).order())); orderedTitles = new ArrayList<>(); for (Field temp : orderedFields) { orderedTitles.add(temp.getAnnotation(ExcelColumn.class).description()); } //如果开启自定义模式 if (customMode) { titleStyle = new ArrayList<>(); contentStyle = new ArrayList<>(); HSSFCellStyle currentCellStyle; Font font; for (Field temp : orderedFields) { currentCellStyle = hssfWorkbook.createCellStyle(); font = hssfWorkbook.createFont(); font.setFontHeightInPoints(temp.getAnnotation(ExcelColumn.class).titleFontSize()); font.setBoldweight(temp.getAnnotation(ExcelColumn.class).titleFontWeight()); font.setColor(temp.getAnnotation(ExcelColumn.class).titleFontColor()); currentCellStyle.setLocked(false); // 表头均不能修改 currentCellStyle.setFont(font); titleStyle.add(currentCellStyle); currentCellStyle = hssfWorkbook.createCellStyle(); font = hssfWorkbook.createFont(); font.setFontHeightInPoints(temp.getAnnotation(ExcelColumn.class).contentFontSize()); font.setBoldweight(temp.getAnnotation(ExcelColumn.class).contentFontWeight()); font.setColor(temp.getAnnotation(ExcelColumn.class).contentFontColor()); currentCellStyle.setLocked(temp.getAnnotation(ExcelColumn.class).columnLocked()); currentCellStyle.setFont(font); contentStyle.add(currentCellStyle); } } } public void titleCellStyleInvoke(HSSFRow row) { HSSFCell titleCell; for (int titleIndex = 0; titleIndex < orderedTitles.size(); titleIndex++) { titleCell = row.createCell(titleIndex); titleCell.setCellStyle(titleStyle.get(titleIndex)); titleCell.setCellValue(orderedTitles.get(titleIndex)); } } public void contentCellStyleInvoke(HSSFSheet hssfSheet, int firstRowNum) throws Exception { HSSFRow currentRow; for (int rowIndex = firstRowNum + 1; rowIndex < data.size(); rowIndex++) { currentRow = hssfSheet.createRow(rowIndex); HSSFCell currentCell; Object obj = data.get(rowIndex); for (int cellIndex = 0; cellIndex < orderedFields.size(); cellIndex++) { currentCell = currentRow.createCell(cellIndex); currentCell.setCellStyle(contentStyle.get(cellIndex)); if (!orderedFields.get(cellIndex).isAccessible()) { orderedFields.get(cellIndex).setAccessible(true); if (orderedFields.get(cellIndex).get(obj) != null) { currentCell.setCellValue(orderedFields.get(cellIndex).get(obj).toString()); } orderedFields.get(cellIndex).setAccessible(false); } else { if (orderedFields.get(cellIndex).get(obj) != null) { currentCell.setCellValue(orderedFields.get(cellIndex).get(obj).toString()); } } } } } public String getSheetName() { return sheetName; } public void setSheetName(String sheetName) { this.sheetName = sheetName; } public List getOrderedTitles() { return orderedTitles; } public void setOrderedTitles(List orderedTitles) { this.orderedTitles = orderedTitles; } public List getData() { return data; } public void setData(List data) { this.data = data; } public Class getTargetClass() { return targetClass; } public void setTargetClass(Class targetClass) { this.targetClass = targetClass; } public List getOrderedFields() { return orderedFields; } public void setOrderedFields(List orderedFields) { this.orderedFields = orderedFields; } public ExcelProcessor getExcelProcessor() { return excelProcessor; } public void setExcelProcessor(ExcelProcessor excelProcessor) { this.excelProcessor = excelProcessor; } public boolean isCustomMode() { return customMode; } public void setCustomMode(boolean customMode) { this.customMode = customMode; } public List getTitleStyle() { return titleStyle; } public void setTitleStyle(List titleStyle) { this.titleStyle = titleStyle; } public List getContentStyle() { return contentStyle; } public void setContentStyle(List contentStyle) { this.contentStyle = contentStyle; } public HSSFWorkbook getHssfWorkbook() { return hssfWorkbook; } public void setHssfWorkbook(HSSFWorkbook hssfWorkbook) { this.hssfWorkbook = hssfWorkbook; } } ``` ## ExcelProcessor ```java public interface ExcelProcessor { /** * 在正式注入数据之前的自定义操作,可以自己设计标题等 * * @param sheet -> 当前 sheet 信息 * @param currentSheet -> 当前 sheet * @return 想要正式注入表头及数据的行数 */ Integer customOperationBeforeInvokeData(Sheet sheet, HSSFSheet currentSheet); /** * 在注入完数据后的自定义操作,如:在表格最后落上日期、对某些数据进行修改等 * 可以通过 currentSheet..getLastRowNum() 获取最后一行的行标,从 0 开始 * * @param sheet -> 当前 sheet 信息 * @param currentSheet -> 当前 sheet */ void customOperationAfterInvokeData(Sheet sheet, HSSFSheet currentSheet, int firstRowNum); } ``` ## StandardExcelProcessor ```java public class StandardExcelProcessor implements ExcelProcessor { @Override public Integer customOperationBeforeInvokeData(Sheet sheet, HSSFSheet currentSheet) { return 0; } @Override public void customOperationAfterInvokeData(Sheet sheet, HSSFSheet currentSheet, int firstRowNum) { ExcelUtil.RowWidthAutoFix(sheet, currentSheet, firstRowNum); } } ``` ## ExcelUtil ```java public class ExcelUtil { /** * 获取 excel 文件创造器 * * @param excelFileName -> excel 文件的名字 * @return */ public static ExcelWorkBookCreator getExcelWorkBookCreator(String excelFileName) { return new ExcelWorkBookCreator(excelFileName); } /** * 列宽自适应,可以在 ExcelProcessor 的实现类的后置处理中使用 * * @param sheet * @param currentSheet * @param firstRowNum */ public static void RowWidthAutoFix(Sheet sheet, HSSFSheet currentSheet, int firstRowNum) { int columnNum = sheet.getOrderedTitles().size(); int columnLength = currentSheet.getLastRowNum() + 1; System.out.println(columnLength); int[] columnWidth = new int[columnNum]; for (int index = 0; index < columnNum; index++) { columnWidth[index] = 0; } HSSFRow currentRow; for (int currentRowNum = firstRowNum; currentRowNum < columnLength; currentRowNum++) { currentRow = currentSheet.getRow(currentRowNum); HSSFCell currentCell; if (currentRow != null) { for (int currentColumnNum = 0; currentColumnNum < columnNum; currentColumnNum++) { currentCell = currentRow.getCell(currentColumnNum); if (currentCell != null && currentCell.getStringCellValue().getBytes().length > columnWidth[currentColumnNum]) { columnWidth[currentColumnNum] = currentCell.getStringCellValue().getBytes().length; } } } } for (int index = 0; index < columnNum; index++) { currentSheet.setColumnWidth(index, columnWidth[index] * 300); } } } ``` ## ExcelWorkBookCreator ```java public class ExcelWorkBookCreator { private String excelFileName; private List sheets; private HSSFWorkbook hssfWorkbook; private final String EXCEL_SHEET_PASSWORD = "123321"; public ExcelWorkBookCreator(String excelFileName) { this.excelFileName = excelFileName; hssfWorkbook = new HSSFWorkbook(); sheets = new ArrayList<>(); } public void addOneSheet(String sheetName, List data, Class targetClass) { sheets.add(new Sheet(hssfWorkbook, sheetName, data, targetClass)); } public void addOneSheet(String sheetName, List data, Class targetClass, boolean customMode) { sheets.add(new Sheet(hssfWorkbook, sheetName, data, targetClass, customMode)); } public void addOneSheet(String sheetName, List data, Class targetClass, ExcelProcessor excelProcessor) { sheets.add(new Sheet(hssfWorkbook, sheetName, data, targetClass, excelProcessor)); } public void addOneSheet(String sheetName, List data, Class targetClass, ExcelProcessor excelProcessor, boolean customMode) { sheets.add(new Sheet(hssfWorkbook, sheetName, data, targetClass, excelProcessor, customMode)); } /** * 导出 excel * * @param response * @return * @throws Exception */ public void excelExport(HttpServletResponse response) throws Exception { //设置文件名 response.setContentType("multipart/form-data"); response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode((excelFileName + ".xls"), "UTF-8")); OutputStream os = response.getOutputStream(); //开始注入数据 for (int sheetIndex = 0; sheetIndex < sheets.size(); sheetIndex++) { Sheet currentSheetMsg = sheets.get(sheetIndex); HSSFSheet currentSheet = hssfWorkbook.createSheet(currentSheetMsg.getSheetName()); currentSheet.protectSheet(EXCEL_SHEET_PASSWORD); ExcelProcessor excelProcessor = currentSheetMsg.getExcelProcessor(); int firstRowNum = excelProcessor.customOperationBeforeInvokeData(currentSheetMsg, currentSheet); if (currentSheetMsg.isCustomMode()) { invokeDataUnderCustomModel(currentSheetMsg, currentSheet, firstRowNum); } else { invokeData(currentSheetMsg, currentSheet, firstRowNum); } excelProcessor.customOperationAfterInvokeData(currentSheetMsg, currentSheet, firstRowNum); } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); hssfWorkbook.write(outputStream); outputStream.flush(); hssfWorkbook.close(); outputStream.close(); InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); int b = 0; byte[] buffer = new byte[512]; while (b != -1) { b = inputStream.read(buffer); if (b != -1) { os.write(buffer, 0, b); } } if (os != null) { os.close(); os.flush(); } } /** * 不开启自定义文本模式下注入正文 * * @param currentSheet * @param hssfSheet * @param firstRowNum * @throws Exception */ private void invokeData(Sheet currentSheet, HSSFSheet hssfSheet, int firstRowNum) throws Exception { //设置表头 HSSFRow firstRow = hssfSheet.createRow(firstRowNum); HSSFCellStyle cellStyle = hssfWorkbook.createCellStyle(); Font titleFont = hssfWorkbook.createFont(); //加粗标题字体 titleFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); cellStyle.setFont(titleFont); cellStyle.setLocked(true); HSSFCell titleCell; for (int titleIndex = 0; titleIndex < currentSheet.getOrderedTitles().size(); titleIndex++) { titleCell = firstRow.createCell(titleIndex); titleCell.setCellValue(currentSheet.getOrderedTitles().get(titleIndex)); titleCell.setCellStyle(cellStyle); } //注入数据 List currentData = currentSheet.getData(); List fields = currentSheet.getOrderedFields(); cellStyle = hssfWorkbook.createCellStyle(); cellStyle.setLocked(true); HSSFRow currentRow; for (int rowIndex = 0; rowIndex < currentData.size(); rowIndex++) { currentRow = hssfSheet.createRow(rowIndex + firstRowNum + 1); HSSFCell currentCell; Object obj = currentData.get(rowIndex); for (int cellIndex = 0; cellIndex < fields.size(); cellIndex++) { currentCell = currentRow.createCell(cellIndex); currentCell.setCellStyle(cellStyle); if (!fields.get(cellIndex).isAccessible()) { fields.get(cellIndex).setAccessible(true); if (fields.get(cellIndex).get(obj) != null) { currentCell.setCellValue(fields.get(cellIndex).get(obj).toString()); } fields.get(cellIndex).setAccessible(false); } else { if (fields.get(cellIndex).get(obj) != null) { currentCell.setCellValue(fields.get(cellIndex).get(obj).toString()); } } } } } /** * 开启自定义文本模式下注入正文 * * @param currentSheet * @param hssfSheet * @param firstRowNum * @throws Exception */ private void invokeDataUnderCustomModel(Sheet currentSheet, HSSFSheet hssfSheet, int firstRowNum) throws Exception { //设置表头 HSSFRow firstRow = hssfSheet.createRow(firstRowNum); currentSheet.titleCellStyleInvoke(firstRow); //注入数据 currentSheet.contentCellStyleInvoke(hssfSheet, firstRowNum); } public String getExcelFileName() { return excelFileName; } public void setExcelFileName(String excelFileName) { this.excelFileName = excelFileName; } public List getSheets() { return sheets; } public void setSheets(List sheets) { this.sheets = sheets; } } ``` # 测试 ```java @Controller @RequestMapping("/test") public class TestController { @ResponseBody @RequestMapping(value = "/getExcel", method = RequestMethod.GET) public String getExcel(HttpServletResponse response){ try { List students = new ArrayList<>(); this.injectData(students); ExcelWorkBookCreator creator = ExcelUtil.getExcelWorkBookCreator("学生信息汇总"); creator.addOneSheet("学生信息", students, Student.class); creator.excelExport(response); return "success"; } catch (Exception e) { e.printStackTrace(); return "error"; } } private void injectData(List students){ students.add(new Student("1", "张三", "xx1小区", "男")); students.add(new Student("2", "李四", "xx2小区", "男")); students.add(new Student("3", "王五", "xx3小区", "女")); students.add(new Student("4", "小明", "xx4小区", "男")); students.add(new Student("5", "小红", "xx5小区", "女")); } class Student{ @ExcelColumn(order = 1, description = "学号") private String id; @ExcelColumn(order = 2, description = "姓名") private String name; @ExcelColumn(order = 4, description = "住址") private String address; @ExcelColumn(order = 3, description = "性别") private String sex; public Student() { } public Student(String id, String name, String address, String sex) { this.id = id; this.name = name; this.address = address; this.sex = sex; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } } } ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200806190041439.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0NzUzNDUx,size_16,color_FFFFFF,t_70)