excel转List 搞懂java反射机制(一) (两种解析方式)
概述
业务开发当中难免遇到excel导入功能,通过本文学习,构建属于自己的导入工具
一.创建注解
import java.lang.annotation.*;
/**
* 文件导入 配合ExcelImportUtils
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ImportColumn {
/**
* 字段在excel中列位置
*/
int index();
/**
* 是否必填
*/
boolean require() default false;
/**
* 字段名称
* @return
*/
String name();
}
2.创建工具类
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* 导入excel 文件转List工具类 配合ImportColumn注解
*/
@Component
@Slf4j
public class ExcelImportUtils {
/**
*
* @param list 需要返回的list
* @param cls 转换对象Class
* @param file excel文件
* @param titles 标题头
* @return 结果
*/
public <T> List<T> convertExcelToList (List<T> list, Class cls, MultipartFile file,String titles){
XSSFWorkbook workbook = null;
try {
workbook = new XSSFWorkbook(file.getInputStream());
if (Objects.isNull(workbook)) {
throw new MyRuntimeException("文件获取失败");
}
} catch (IOException e) {
log.error("file转换workbook发生io异常:{}",e.getMessage());
}
return convertExcelToList ( list, cls, workbook, titles);
}
/**
* 一个文件需要转多个文件 建议调取这个方法,减少读取文件的时间
* @param list 需要返回的list
* @param cls 转换对象Class
* @param workbook excel文件
* @param titles 标题头
* @return 结果
*/
public <T> List<T> convertExcelToList (List<T> list, Class cls, XSSFWorkbook workbook,String titles){
try {
//遍历sheet
XSSFSheet sheet = workbook.getSheetAt(0);//获取当前sheet对象
if (sheet.getPhysicalNumberOfRows() > 1) {//如果行数据不为空
for (int j = 0; j < sheet.getPhysicalNumberOfRows(); j++) {//遍历行信息
XSSFRow row = sheet.getRow(j);
if (j != 0) {
//去掉整行的数据都是空的 直接返回结果
if (StringUtils.isEmpty(row.getCell(0).getStringCellValue().trim())) {
return list;
}
}
//构建对象
list.add((T) buildEntity( cls, row));
if (j == 0) {//获取第一行标题,判断模板是否正确
boolean isRigtTemplate = checkTitle(row, titles);
if (!isRigtTemplate) {
throw new MyRuntimeException("模板不正确,请下载模板完善后上传");
}
}
}
}
}catch (Exception e){
log.error("convertExcelToList发生io异常:{}",e.getMessage());
throw new MyRuntimeException(e.getMessage());
}
return list;
}
/**
* 构建参数
* @param cls
* @param row
* @return
*/
public Object buildEntity(Class cls,XSSFRow row){
Object obj = null;
try {
Constructor<?> constructor = cls.getDeclaredConstructor();
//忽略修饰访问所有字段与方法
constructor.setAccessible(true);
//创建实例
obj = constructor.newInstance();
//通过反射获取方法成员变量名称 getDeclaredFields 忽略修饰符
Field[] filds = cls.getDeclaredFields();
//遍历字段赋值
for(int index = 0;index < filds.length; index++){
Field field = filds[index];
//获取ImportColumn 注解 如果没有绕过
ImportColumn[] importColumn = field.getAnnotationsByType(ImportColumn.class);
if(Objects.nonNull(importColumn) && importColumn.length > 0){
//获取当前格子值
Object value = getValue( field.getGenericType().getTypeName(),
row.getCell(importColumn[0].index()));
//校验是否必填
if(importColumn[0].require() && Objects.isNull(value)){
throw new MyRuntimeException(String.format("必填字段%s为null",importColumn[0].name()));
}
//为字段赋值
field.set(obj, value);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
/**
* 根据类型名称获取值
* @param typeName 字段类型名称
* @param cell 单元格
* @return
*/
private Object getValue(String typeName, XSSFCell cell){
Object value = null;
switch (typeName){
case "java.lang.Integer"://处理int类型字段
value = getInteger(cell);
break;
case "java.util.String"://处理字符串
value = cell.getStringCellValue();
break;
case "java.util.Date"://处理日期
value = getDateValue(cell);
break;
case "java.math.BigDecimal"://处理 BigDecimal
value = getBigDecimal(cell);
break;
case "java.lang.Double"://处理 Double
value = getDouble(cell);
break;
case "java.lang.Long"://处理 Long
value = getLong(cell);
break;
case "boolean"://处理 boolean
value = getBoolean(cell);
break;
case "int"://处理 int
value = Optional.ofNullable(getInteger(cell)).orElse(0);
break;
case "double"://处理 double
value = Optional.ofNullable(getDouble(cell)).orElse(0.0);
break;
case "long"://处理 long
value = Optional.ofNullable(getLong(cell)).orElse(0L);
break;
}
return value;
}
/**
* 校验表头
* @param firstRow
* @param titles
* @return
*/
private boolean checkTitle(XSSFRow firstRow, String titles) {
boolean flag = true;
String[] titleArr = titles.split(",");
for (int i = 0; i < titleArr.length; i++) {
if (!titleArr[i].equals(firstRow.getCell(i).getStringCellValue())) {
flag = false;
break;
}
}
return flag;
}
private Date getDateValue(XSSFCell cell){
try {
return cell.getDateCellValue();
}catch (Exception e){
return null;
}
}
private BigDecimal getBigDecimal(XSSFCell cell){
try {
String cellvalue = cell.getStringCellValue();
return StringUtils.isEmpty(cellvalue) ? null :BigDecimal.valueOf(Double.parseDouble(cellvalue));
}catch (Exception e){
return null;
}
}
private Integer getInteger(XSSFCell cell){
try {
return (int)cell.getNumericCellValue();
}catch (Exception e){
return null;
}
}
private Double getDouble(XSSFCell cell){
try {
return cell.getNumericCellValue();
}catch (Exception e){
return null;
}
}
private Long getLong(XSSFCell cell){
try {
Double cellValue = cell.getNumericCellValue();
return cellValue.longValue();
}catch (Exception e){
return null;
}
}
private boolean getBoolean(XSSFCell cell){
try {
return cell.getBooleanCellValue();
}catch (Exception e){
return false;
}
}
}
3.调用方式
List<MyOrder> list = new ArrayList<>();
list = excelImportUtils.convertExcelToList(list,MyOrder.class,file,"");
4.总结
本文介绍的是固定模板上传,下文介绍通过标题导入 有问题欢迎指正