背景:
使用easypoi 作为导入导出框架,引入的版本号是:
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.0.0</version>
</dependency>
使用场景:
使用@Excel 导出实体类信息,通常我们都会通过存储枚举的编码到数据库中,而枚举值则放在代码的枚举类里。但是在我们导出的时候,则需要将枚举文字信息导出到表格里。
根据easypoi现有模式的实现:
@Excel的属性值里使用replace,把所有的枚举值需要按照他既定的格式,再重新设置一遍。
痛点:如果枚举个数过多,需要大量重复且繁琐的配置。
针对重复冗余的动作,我们针对这个进行改造,先看效果:
1. 通过自定义注解,设置对应的枚举类型,实现自动转换枚举信息;如果你的枚举类,key 和value 都一致,也可以省略指定key,和value;
2. 在导出的时候显式调用工具类,即可自动注入
List<Class> classes = new ArrayList<>();
classes.add(ContractExcelVO.class);
ExcelPojoUtils.initClassExcel(classes);
实现过程:
1.定义一个自定义注解类@ColumnExcelEnum,定义枚举的基本相关信息。
/**
* @description:导出表格的枚举值处理方式注解,
* 用于导出表格时,对枚举值的处理方式,如:是否是外部枚举,枚举的键属性名称,枚举的值属性名称等等
* @author: hhq
* @create: 2020-11-02 18:35
**/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
@Documented
public @interface ColumnExcelEnum {
/**
* 对应的是否是外部枚举
* @return
*/
boolean externalEnum() default false;
/**
* 对应的枚举值名称
* @return
*/
String name() default "";
/**
* 枚举的键属性名称
* @return
*/
String key() default "";
/**
* 枚举的值属性名称
* @return
*/
String value() default "";
}
2.通过传入的类名,使用反射的方式,获取属性上注入自定义注解的字段。再对属性上的@Excel 注解进行解析,动态的对@Excel 注入属性replace。
InvocationHandler handler = Proxy.getInvocationHandler(excel);
3.下面是我自己封装的一个使用自定义注解动态替换枚举值的工具类
package com.pindao.developproject.newContract.utils;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.pindao.developproject.newContract.annotation.ColumnExcelEnum;
import com.pindao.developproject.newContract.common.Constant;
import io.swagger.models.Contact;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @description:表格导出的枚举值转化处理器
* @author: hhq
* @create: 2022-07-01 14:47
**/
@Component
@Slf4j
public class ExcelPojoUtils {
/**
* 动态修改类信息
*
* @param classList
*/
public static void initClassExcel(List<Class> classList) {
classList.forEach(clazz -> {
dynamicConvertEnum(clazz);
});
}
/**
* 通过类名转化枚举值
*
* @param clazz
*/
public static void dynamicConvertEnum(Class clazz) {
Field[] fields = clazz.getDeclaredFields();
//如果存在的话,且有枚举类型的话,动态修改,自动转化
for (Field field : fields) {
ColumnExcelEnum columnEnum = field.getAnnotation(ColumnExcelEnum.class);
if (ObjectUtils.isEmpty(columnEnum)) {
continue;
}
String enumName = columnEnum.name();
if (!StringUtils.isEmpty(enumName)) {
Excel excel = field.getAnnotation(Excel.class);
InvocationHandler handler = Proxy.getInvocationHandler(excel);
try {
Field anField = handler.getClass().getDeclaredField("memberValues");
anField.setAccessible(true);
Map memberMap = (Map) anField.get(handler);
memberMap.put("replace", convertReplace(columnEnum));
} catch (Exception e) {
log.error("未找到该属性值", e);
}
}
}
}
public static String[] convertReplace(ColumnExcelEnum columnEnum) {
try {
Class enumClass = null;
String className = columnEnum.externalEnum() ? columnEnum.name() : Constant.ENUM_CURRENT_PATH + columnEnum.name();
enumClass = Class.forName(className);
Object[] objects = enumClass.getEnumConstants();
Method getCodeMethod = enumClass.getMethod(columnEnum.key());
Method getValueMethod = enumClass.getMethod(columnEnum.value());
String[] replaces = new String[objects.length + 1];
int i = 0;
for (Object obj : objects) {
replaces[i] = getValueMethod.invoke(obj) + Constant.STRING_SPLIT_UNLINE + getCodeMethod.invoke(obj);
i++;
}
replaces[i] = Constant.EXCEL_NULL;
return replaces;
} catch (Exception e) {
log.error("转换对应的枚举值失败", e);
}
return null;
}
}
4.工具类中使用到的常量
/**
* 字符串分隔符
*/
public final static String STRING_SPLIT_UNLINE ="_";
/**
* 导出的枚举空值处理方式
*/
public final static String EXCEL_NULL ="_null";
/**
* 本地枚举值的路径
*/
public static final String ENUM_CURRENT_PATH = "com.pindao.developproject.newContract.enums.";