EasyExcel之动态表头导出不生效

今天接到一个优化需求,表格导出后的表头顺序和页面不一致,要优化成一致的。根据传入的字段,动态导出数据,并保证顺序。
我看到导出的实体类都有@ExcelProperty注解,同时也在官网查看了这注解的含义和使用。

@ExcelProperty有两个属性可以帮我们排序:index和order,所以我就想每次在去写excel的时候,对映射类字段的index去动态排序。
注意index的使用

  • index的值相同时会抛出异常
  • index的值不连续时会插入空白列

然后想直接用反射动态修改index

    /**
     *
     * @param headList 前端上送的动态表头
     * @param clazz 导出实体类的class对象
     */
    private static void handleExcelHead(List<String> headList, Class<?> clazz) {
        try {
            for (String excelHead : headList) {
                Field declaredField = clazz.getDeclaredField(excelHead);
                ExcelProperty annotation = declaredField.getAnnotation(ExcelProperty.class);
                InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
                Field field = invocationHandler.getClass().getDeclaredField("memberValues");
                field.setAccessible(true);
                Map<String, Object> memberValues = (Map<String, Object>) field.get(invocationHandler);
                memberValues.put("index", headList.indexOf(excelHead));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

完整代码如下:

public class Test {
    public static void main(String[] args) throws Exception {
        List<String> headList = new ArrayList<>();
        headList.add("name");
        headList.add("sex");
        headList.add("age");
        // 动态处理表头
        handleExcelHead(headList,User.class);

        EasyExcel.write("test.xlsx").head(User.class)
                .sheet("test")
                .includeColumnFiledNames(headList)
                .doWrite(init());
    }
    /**
     *
     * @param headList 前端上送的动态表头
     * @param clazz 导出实体类的class对象
     */
    private static void handleExcelHead(List<String> headList, Class<?> clazz) {
        try {
            for (String excelHead : headList) {
                Field declaredField = clazz.getDeclaredField(excelHead);
                ExcelProperty annotation = declaredField.getAnnotation(ExcelProperty.class);
                InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
                Field field = invocationHandler.getClass().getDeclaredField("memberValues");
                field.setAccessible(true);
                Map<String, Object> memberValues = (Map<String, Object>) field.get(invocationHandler);
                memberValues.put("index", headList.indexOf(excelHead));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private static List<User> init() {

        List<User> userList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setName("test"+i);
            user.setSex("男");
            user.setAge(20 + i);
            userList.add(user);
        }

        return userList;
    }
}

代码运行后发现和预期结果一样。但是当我则调整表头顺序并增加表头后发现不生效了。后来debug发现由于使用的是class对象,只要这个jvm不重启或者这个对象不被回收修改的index就一直存在,所以我们每次应该把index恢复成默认值。但是发现还是不行。后来在想这框架应该是使用了缓存吧,毕竟导出实体是固定的。果然:

ClassUtils 这里使用了缓存

    public static final Map<Class<?>, FieldCache> FIELD_CACHE = new ConcurrentHashMap<>();


    private static FieldCache declaredFields(Class<?> clazz) {
        if (clazz == null) {
            return null;
        }
        return FIELD_CACHE.computeIfAbsent(clazz, key -> {
            List<Field> tempFieldList = new ArrayList<>();
            Class<?> tempClass = clazz;
            // When the parent class is null, it indicates that the parent class (Object class) has reached the top
            // level.
            while (tempClass != null) {
                Collections.addAll(tempFieldList, tempClass.getDeclaredFields());
                // Get the parent class and give it to yourself
                tempClass = tempClass.getSuperclass();
            }
            // Screening of field
            Map<Integer, List<Field>> orderFieldMap = new TreeMap<Integer, List<Field>>();
            Map<Integer, Field> indexFieldMap = new TreeMap<Integer, Field>();
            Map<String, Field> ignoreMap = new HashMap<String, Field>(16);

            ExcelIgnoreUnannotated excelIgnoreUnannotated = clazz.getAnnotation(ExcelIgnoreUnannotated.class);
            for (Field field : tempFieldList) {
                declaredOneField(field, orderFieldMap, indexFieldMap, ignoreMap, excelIgnoreUnannotated);
            }
            return new FieldCache(buildSortedAllFieldMap(orderFieldMap, indexFieldMap), indexFieldMap, ignoreMap);
        });
    }

这下难住我了,这个缓存也不刷新。后来想他把缓存放在map里,key是Class,我直接每次都给他remove不就可以了。加了一行代码放在处理表头前边,果然可行!

ClassUtils.FIELD_CACHE.remove(User.class);
  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SpringBoot是一个非常流行的开发框架,它为开发者提供了快速、简单的方式来构建Java应用程序。而EasyExcel是一个基于Java的强大的Excel读写框架,能够帮助开发者快速、高效地处理Excel文件。将SpringBootEasyExcel结合起来,可以实现动态表头导出的功能。 首先,我们需要在SpringBoot项目中引入EasyExcel的依赖,这可以通过在pom.xml文件中添加相应的依赖来实现。接着,我们需要编写相应的Java代码来实现动态表头导出的功能。首先,我们需要定义一个实体类,用来表示要导出的数据结构。然后,我们需要编写一个Controller来处理导出请求,并在其中调用EasyExcel的API来实现Excel文件的导出操作。 在实现动态表头导出的过程中,我们需要动态生成表头信息,这可以通过使用EasyExcel的注解来实现。我们可以根据导出数据的结构动态地生成表头信息,并将其与实际的数据一起写入Excel文件中。这样,就能够实现动态表头导出的功能了。 除了上述的步骤之外,我们还需要进行一些配置工作,例如配置Excel文件的格式、样式等。通过这些配置,我们可以实现导出Excel文件符合我们的实际需求。 总的来说,SpringBoot整合EasyExcel进行动态表头导出,需要引入EasyExcel的依赖,编写相应的Java代码,处理导出请求,并进行相应的配置工作。通过这样的方式,我们就能够实现动态表头导出的功能,使得导出Excel文件能够满足我们的实际需求。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值