若依框架 excel导出功能详解(一)
若依框架作为被广泛应用的二次开发框架,其在excel导出方面用到了很多相关api,接下来就详细讲解一下若依框架的导出功能执行过程,以及老版本存在的bug(3.8.8及以前)。
调用方式
ExcelUtil<类名> util = new ExcelUtil<RmsReport>(类名.class);
util.exportExcel(response, list, 名称);
public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title)
{
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
this.init(list, sheetName, title, Type.EXPORT);
exportExcel(response);
}
第一段是导出的调用方式,类名就是所对应的实体类名。第二段是倒数第二步的背后调用,这一步分为两阶段,首先是初始化即 this.init方法,然后是将数据写入即exportExcel方法。
初始化 init方法
public void init(List<T> list, String sheetName, String title, Type type)
{
if (list == null)
{
list = new ArrayList<T>();
}
this.list = list;
this.sheetName = sheetName;
this.type = type; //Type.EXPORT代表用来输出
this.title = title;//StringUtils.EMPTY 默认为空
createExcelField();//创建表中字段
createWorkbook();//创建工作簿以及工作表
createTitle();//填写标题(系统默认title参数为空,则此函数会判断为标题为空后直接跳过)
createSubHead();//填写子标题(针对字段中包含子对象list)
}
初始化即是上面的代码,通过注释可知初始化主要是做准备工作,这四个函数中最重要的是第一个函数,因为获得的字段中是否包含了子对象列表,决定了后续填写子标题函数是否执行,以及影响第二阶段数据的写入。所以重点介绍createExcelField()方法。
private void createExcelField()
{
this.fields = getFields();//获得字段
this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());//根据excel注释中的sort属性进行排序
this.maxHeight = getRowHeight();//根据excel注释中的height属性获得最高的高度作为表格高度。
}
public List<Object[]> getFields()
{
List<Object[]> fields = new ArrayList<Object[]>();
List<Field> tempFields = new ArrayList<>();
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
for (Field field : tempFields)
{
if (!ArrayUtils.contains(this.excludeFields, field.getName()))
{
// 单注解
if (field.isAnnotationPresent(Excel.class))
{
Excel attr = field.getAnnotation(Excel.class);
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
{
field.setAccessible(true);
fields.add(new Object[] { field, attr });
}
if (Collection.class.isAssignableFrom(field.getType()))
{
//获得字段的get方法,用于后续写入数据时获得对应的值。
subMethod = getSubMethod(field.getName(), clazz);
// 获取字段的泛型类型。如果字段是带泛型的集合(如List<String>),则返回 List<String>
ParameterizedType pt = (ParameterizedType) field.getGenericType()
//获取泛型参数的实际类型。比如List<String>,这会返回String的Class对象。[0]表示第一个泛型参数(集合通常只有一个泛型参数,即元素类型)。
Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
//Apache Commons Lang的FieldUtils,用于获取类中带有Excel注解的字段列表。
this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
}
}
// 多注解
if (field.isAnnotationPresent(Excels.class))
{
Excels attrs = field.getAnnotation(Excels.class);
Excel[] excels = attrs.value();
for (Excel attr : excels)
{
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
{
field.setAccessible(true);
fields.add(new Object[] { field, attr });
}
}
}
}
}
return fields;
}
getFields()是创建excel字段数中的核心方法,即获得实体类的字段,这里面主要利用了java反射的相关方法,
- 新建一个object数组变量的list,这个list是函数的返回值,主要是用来存储字段和对应的excel注释,然后是新建一个存储字段的列表,用来存储实体类的字段,实体类的字段通过getDeclaredFields()获得。
- 遍历此列表中的字段,利用isAnnotationPresent方法判断是否含有excel注解,这里分为excel和excels两种注解,一般情况下是用excel即单注解,因此着重介绍单注解的判断。
- 单注解首先获得excel注解,然后对excel注解进行判断,包含非空判断和类型判断,其中类型判断十分重要,excel注解的类型是一个枚举类,枚举变量有三种分别为all,EXPORT,IMPORT。一般情况下excel注释默认为all,判断为true,或者为EXPORT也可以判断为true,如果只想将一个字段导入而不是导出,则 将type设为IMPORT。判断后则是将字段和excel注释存储到新建的object数组中,并添加到之前新建的list中。最后是判断字段是否为集合类型的变量,如果是的话则需要获得集合中的变量类型,并获得相应字段。if里的执行语句即获得过程,详细可以看代码上的注解。最后多注解跟单注解类似,只不过注释由excel转变成了由多个excel注释组成的excles注释。