基于反射的excel映射插件(直接把list集合中的对象映射成excel表)

项目中经常需要导出excel的表格,但是基本上都是调用第三方的插件来进行一个直接调用。我们能不能通过反射写一个通用的excel的插件呢?

1. 依赖引入:

<!-- 处理Excel相关-->  
<dependency>  
    <groupId>org.apache.poi</groupId>  
    <artifactId>poi</artifactId>  
    <version>3.17</version>  
</dependency>

2. 方法参数的思考。

我们能不能定义一种规范,只有符合这种规范的实体类才能被映射成excel表?
解决方法:自定义接口,只有实现这个接口的类才能被excel化。参考了serializable的空实现,我们这里可以直接空实现,到时候需要公共的方法的时候,可以方便的进行扩展。
接口代码:

/**  
 * @author: TMingYi
 * @date: 2020/12/12 19:50 */// 所有需要excel序列化的类,都必须实现这个接口。  
public interface Excelable extends Serializable {  
}

3. 接口定义

/**  
 * @param headMsg excel表头的信息。  
  * @param targetClass 目标类的class对象。  
  * @param list 目标类集合()  
  */  
public static void getExcelByObjectList(  
        String headMsg,  
        Class<? extends Excelable> targetClass,  
        List<? extends Excelable > list,  
        File file,  
        Map<String,String> map){  
	 // 具体的代码 
}
  • headMsg:excel的标题(第一行的描述文字),由外部传入。
  • targetClass:目标类的Class对象,方便我们反射获取值。
  • list:目标对象的集合。
  • file:具体生成的文件。
  • map:类里面的字段名(驼峰)和中文意思的对应。由外部定义。

4. 如何根据字段长度合并单元格?

我们第一行为描述信息,需要根据现有的字段的个数来合并单元格,那么久必须知道目前对象的字段有多少个?反射就可以帮我们完成。

// 获取字段的长度
Field[] fields = targetClass.getDeclaredFields();  
int fieldLength = fields.length;
// 创建excel的代码(略);
// 我们根据字段的长度来设置我们的合并单元格长度。  
sheet.addMergedRegion(new CellRangeAddress(0,0,0,fieldLength - 1));

5. 字段名映射成表头

通过反射获取字段的Name,然后在map中寻找即可。

for (int i = 0 ; i < fieldLength ; i++){  
    // 使用field字段的值来初始化我们的表格头信息;  
  HSSFCell cell = row2.createCell(i);  
    // 根据Map设置我们的头顶的值。  
  cell.setCellValue(  
            map.get(fields[i].getName()) == null ?  
                    fields[i].getName() :  
                    map.get(fields[i].getName()));  
    cell.setCellStyle(innerStyle);  
    sheet.setColumnWidth(i,5000);  
}

6. 如何遍历集合?

通过循环,每遍历一行就创建一个HSSFRow,这里会调用字段的tostring方法进行填充单元格。也可以在抽象接口中定义特定的方法进行返回值,我这里做了一个处理,如果发现字段是Date的,就调用SimpleDateFormat进行一个格式化的操作。

// 填充我们实际的值!  
  for (int i = 0 ; i < list.size() ; i++){  
            HSSFRow tempRow = sheet.createRow(i + 2);  
  
            for (int j = 0 ; j < fieldLength ; j++){  
                // 使用field字段的值来初始化我们的表格头信息;  
             try {  
                    // 把所有字段都设置成可读的。  
                    fields[j].setAccessible(true);  
                    HSSFCell cell = tempRow.createCell(j);  
                  // 对日期类型做一个特殊的处理。  
                   if ((fields[j].get(list.get(i))) instanceof Date){  
                        // 对日期类型进行格式化;  
  cell.setCellValue(SIMPLE_DATE_FORMAT.format((Date)(fields[j].get(list.get(i)))));  
                    }else {  
                        cell.setCellValue(fields[j].get(list.get(i)).toString());  
                    }  
                    cell.setCellStyle(innerStyle);  
                } catch (IllegalAccessException e) {  
                    e.printStackTrace();  
                    throw new RuntimeException( "fields value not toString!");  
                }  
            }  
        }

至于设置单元格样式,可以参考文章最后的完整代码。
最终效果(标题的时间是在代码里面加的):
在这里插入图片描述

创造了excel之后,可以通过HTTP 协议直接返回给浏览器下载:

/**  
 * @param trainingId 实训信息的id;  
 * @param response 响应体包含excel对象。  
  */  
@GetMapping("get/studentTable/{trainingId}")  
@RequireRole(RoleConst.ROLE_TYPE_ADMIN)  
public void getStudentTable(HttpServletResponse response) {  
    // 获得list代码(略)
    // 设置响应头,默认下载。  
  response.setHeader("Content-disposition", "attachment;filename=666.xls");//默认Excel名称  
  try {  
        ExcelUtil.getExcelByObjectList(  
                "攀枝花学院数学与计算机学院实训名单" ,  
                StudentInfoVO.class,  
                resuls,response.getOutputStream(),  
                StudentConst.STUDENT_MAP);  
        response.flushBuffer();  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  
}

完整代码:

package sjjx.manage.util;  
  
import org.apache.ibatis.annotations.Param;  
import org.apache.poi.hssf.usermodel.*;  
import org.apache.poi.ss.usermodel.HorizontalAlignment;  
import org.apache.poi.ss.util.CellRangeAddress;  
import sjjx.manage.config.mypinterface.Excelable;  
  
import java.io.File;  
import java.io.IOException;  
import java.io.OutputStream;  
import java.lang.reflect.Field;  
import java.text.SimpleDateFormat;  
import java.util.Arrays;  
import java.util.Date;  
import java.util.List;  
import java.util.Map;  
import java.util.concurrent.Future;  
  
/**  
 * @author TMingYi * @classname ExcelUtil * @description 处理Excel相关的方法;  
 * @date 2020/12/12 19:36 */public class ExcelUtil {  
  
    private static final String START_CHAR = "(";  
  
    private static final String END_CHAR = ")";  
  
    private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");  
  
  
    /**  
 * @param headMsg excel表头的信息。  
  * @param targetClass 目标类的class对象。  
  * @param list 目标类集合()  
  * @return 包含目标对象的 Futrue;  
 */  public static void getExcelByObjectList(  
            String headMsg,  
            Class<? extends Excelable> targetClass,  
            List<? extends Excelable > list,  
            OutputStream stream,  
            Map<String, String> map){  
        // 获得表单对象。  
  HSSFWorkbook wk = getHSSFWorkBook(headMsg,targetClass,list, map);  
  
        try {  
            wk.write(stream);  
        } catch (IOException e) {  
            e.printStackTrace();  
            throw new RuntimeException( "transform stream fail");  
        }  
    }  
  
  
    /**  
 * @param headMsg excel表头的信息。  
  * @param targetClass 目标类的class对象。  
  * @param list 目标类集合()  
  */  
  public static void getExcelByObjectList(  
            String headMsg,  
            Class<? extends Excelable> targetClass,  
            List<? extends Excelable > list,  
            File file,  
            Map<String,String> map){  
        HSSFWorkbook wk = getHSSFWorkBook(headMsg,targetClass,list,map);  
        try {  
            wk.write(file);  
        } catch (IOException e) {  
            e.printStackTrace();  
            throw new RuntimeException( "transform stream file");  
        }  
    }  
  
    private static HSSFWorkbook getHSSFWorkBook(  
            String headMsg, Class<? extends Excelable> targetClass,  
            List<? extends Excelable> list, Map<String, String> map) {  
        // 获取所有字段的值。  
  Field[] fields = targetClass.getDeclaredFields();  
        int fieldLength = fields.length;  
        headMsg = wraperTime(headMsg);  
  
        // 创建一个excel;  
  HSSFWorkbook wk = new HSSFWorkbook();  
  
        // 创建一个工作表;  
  HSSFSheet sheet = wk.createSheet("信息表");  
  
        // 创建第一行;  
  HSSFRow row1 = sheet.createRow(0);  
        row1.setHeight((short) 500);  
  
        HSSFCell cell1 = row1.createCell(0);  
        cell1.setCellValue(headMsg);  
  
        // 设置头部字体的样式  
  HSSFCellStyle headStyle = wk.createCellStyle();  
        HSSFFont headFont = wk.createFont();  
        headFont.setBold(true);  
        headFont.setFontHeightInPoints((short) 16);  
        headStyle.setFont(headFont);  
        headStyle.setAlignment(HorizontalAlignment.CENTER);  
        cell1.setCellStyle(headStyle);  
        // 设置样式结束。  
  // 进行单元格的合并  
  // 我们根据字段的长度来设置我们的合并单元格长度。  
  sheet.addMergedRegion(new CellRangeAddress(0,0,0,fieldLength - 1));  
  
        // 设置表格里面数据的样式!  
  HSSFCellStyle innerStyle = wk.createCellStyle();  
        HSSFFont innerFont = wk.createFont();  
        innerFont.setFontHeightInPoints((short) 12);  
        innerStyle.setFont(innerFont);  
        innerStyle.setAlignment(HorizontalAlignment.CENTER);  
  
        // 创建第二行,用于放置头信息。  
  HSSFRow row2 = sheet.createRow(1);  
  
        for (int i = 0 ; i < fieldLength ; i++){  
            // 使用field字段的值来初始化我们的表格头信息;  
  HSSFCell cell = row2.createCell(i);  
            // 根据Map设置我们的头顶的值。  
  cell.setCellValue(  
                    map.get(fields[i].getName()) == null ?  
                            fields[i].getName() :  
                            map.get(fields[i].getName()));  
            cell.setCellStyle(innerStyle);  
            sheet.setColumnWidth(i,5000);  
        }  
  
  
        // 填充我们实际的值!  
  for (int i = 0 ; i < list.size() ; i++){  
            HSSFRow tempRow = sheet.createRow(i + 2);  
  
            for (int j = 0 ; j < fieldLength ; j++){  
                // 使用field字段的值来初始化我们的表格头信息;  
  try {  
                    // 把所有字段都设置成可读的。  
  fields[j].setAccessible(true);  
                    HSSFCell cell = tempRow.createCell(j);  
// 对日期类型做一个特殊的处理。  
  if ((fields[j].get(list.get(i))) instanceof Date){  
                        // 对日期类型进行格式化;  
  cell.setCellValue(SIMPLE_DATE_FORMAT.format((Date)(fields[j].get(list.get(i)))));  
                    }else {  
                        cell.setCellValue(fields[j].get(list.get(i)).toString());  
                    }  
                    cell.setCellStyle(innerStyle);  
                } catch (IllegalAccessException e) {  
                    e.printStackTrace();  
                    throw new RuntimeException( "fields value not toString!");  
                }  
            }  
        }  
        return wk;  
    }  
  
    // 给我们的头部包装一个时间戳  
  private static String wraperTime(String headMsg) {  
        String nowTime = DateUtil.getTimeNow("yyyy-MM-dd");  
        return headMsg + START_CHAR + nowTime + END_CHAR;  
    }  
}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页