[alibaba/easyexcel]AOP动态修改列名失效

2024-03-07 752 views
2

我使用AOP将@ExcelProperty中的列名修改,用来支持国际化,例如这样 @ExcelProperty("${file.name}") private String name; 本地测试好了之后部署在服务器也没有问题,但是过了一段时间之后就会失效,本来应该导出为 文件名/FileName, 就会失效变成 ${file.name}。本人菜鸟,请教一下有没有和我一样的,或者帮我看看代码有什么问题吗? @Aspect @Component @Order(2) public class ExcelExportAop {

private final MessageSource messageSource;

private final HttpServletRequest request;

public static final String PAGE_SIZE = "pageSize";
public static final String PAGE = "page";

public ExcelExportAop(MessageSource messageSource, HttpServletRequest request) {
    this.messageSource = messageSource;
    this.request = request;
}

@Around(value = "@annotation(excelExport)")
public Object excelExport(ProceedingJoinPoint joinPoint, ExcelExport excelExport) throws Throwable {
    try {
        HttpServletResponse response = null;
        ExportParam exportParam = null;

        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg instanceof HttpServletResponse) {
                response = (HttpServletResponse) arg;
            } else if (arg instanceof ExportParam) {
                exportParam = (ExportParam) arg;
            }
        }

        if (exportParam == null || exportParam.getExportType() == null || !ExportType.match(exportParam.getExportType())) {
            return joinPoint.proceed();
        }

        String i18n = request.getHeader("i18n");
        String language = i18n.substring(0, i18n.indexOf('-'));
        String country = i18n.substring(i18n.indexOf('-') + 1);
        Field[] declaredFields = excelExport.value().getDeclaredFields();
        List<String[]> originalHeaders = new ArrayList<>();
        for (Field field : declaredFields) {
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            String[] headers;
            if (excelProperty != null) {
                headers = excelProperty.value();
                originalHeaders.add(headers);
                String[] collect = Arrays.stream(headers).map(header -> {
                    String i18nCode = this.resolveExcelExportHeader(header);
                    return messageSource.getMessage(i18nCode, null, new Locale(language, country));
                }).toArray(String[]::new);

                InvocationHandler invocationHandler = Proxy.getInvocationHandler(excelProperty);
                Field memberValue = invocationHandler.getClass().getDeclaredField("memberValues");
                memberValue.setAccessible(true);
                Map map = (Map) memberValue.get(invocationHandler);
                map.put("value", collect);
            } else {
                originalHeaders.add(null);
            }
        }

        Assert.notNull(response, "HttpServletResponse must not be null.");

        if (ExportType.CURRENT.equals(exportParam.getExportType())) {
            doExportCurrentPage(exportParam, joinPoint, excelExport, response);
        } else  {
            doExportAll(exportParam, joinPoint, excelExport, response);
        }

        for (int i = 0; i < declaredFields.length; i++) {
            ExcelProperty excelProperty = declaredFields[i].getAnnotation(ExcelProperty.class);
            if (excelProperty != null) {
                InvocationHandler invocationHandler = Proxy.getInvocationHandler(excelProperty);
                Field memberValue = invocationHandler.getClass().getDeclaredField("memberValues");
                memberValue.setAccessible(true);
                Map map = (Map) memberValue.get(invocationHandler);
                map.put("value", originalHeaders.get(i));
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

private void doExportAll(ExportParam exportParam, ProceedingJoinPoint joinPoint, ExcelExport excelExport, HttpServletResponse response) throws Throwable {
    Object[] args = joinPoint.getArgs();
    MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    List<String> paramNameList = Arrays.asList(methodSignature.getParameterNames());
    if (paramNameList.contains(PAGE_SIZE)) {
        int position = paramNameList.indexOf(PAGE_SIZE);
        args[position] = excelExport.maxDataCount();
    } else {
        for (Object arg : args) {
            for (Field field : arg.getClass().getDeclaredFields()) {
                if (PAGE_SIZE.equals(field.getName())) {
                    field.setAccessible(true);
                    field.set(arg, excelExport.maxDataCount());
                    break;
                }
            }
        }
    }
    if (paramNameList.contains(PAGE)) {
        int position = paramNameList.indexOf(PAGE);
        args[position] = 1;
    } else {
        for (Object arg : args) {
            for (Field field : arg.getClass().getDeclaredFields()) {
                if (PAGE.equals(field.getName())) {
                    field.setAccessible(true);
                    field.set(arg, 1);
                    break;
                }
            }
        }
    }
    Object result = joinPoint.proceed(args);
    returnValueProcess(exportParam, response, result, excelExport);
}

private void doExportCurrentPage(ExportParam exportParam, ProceedingJoinPoint joinPoint, ExcelExport excelExport, HttpServletResponse response) throws Throwable {
    Object result = joinPoint.proceed();
    returnValueProcess(exportParam, response, result, excelExport);
}

private void returnValueProcess(ExportParam exportParam, HttpServletResponse response, Object result, ExcelExport excelExport) throws Exception {
    List<?> list;
    if (result instanceof List<?>) {
        list = (List<?>) result;
        ExportExcelUtil.exportExcel(response, excelExport.value(), exportParam.getExportFileName(), list);
    } else if (result instanceof DataResult) {
        DataResult<?> dataResult = (DataResult<?>) result;
        Object data = dataResult.getData();
        if (data instanceof List<?>) {
            list = (List<?>) data;
            ExportExcelUtil.exportExcel(response, excelExport.value(), exportParam.getExportFileName(), list);
        } else if (data instanceof PageDataVo) {
            PageDataVo<?> pageDataVo = (PageDataVo<?>) data;
            list = pageDataVo.getList();
            ExportExcelUtil.exportExcel(response, excelExport.value(), exportParam.getExportFileName(), list);
        } else {
            throw new Exception("Export data must be instance of List<?> or DataResult<list<?>> or DataResult<PageDataVo>.");
        }
    } else {
        throw new Exception("Export data must be instance of List<?> or DataResult<list<?>> or DataResult<PageDataVo>.");
    }
}

public String resolveExcelExportHeader(String header){
    if (header.startsWith("${") && header.endsWith("}")) {
        int beginIndex = header.indexOf("{");
        int endIndex = header.indexOf("}");
        header = header.substring(beginIndex + 1, endIndex);
    }
    return header;
}

} image

回答

5

不建议用这种黑科技呀 动态列名直建议接用api head(List<List> head) head(Class<?> clazz)

6

你是指这个吗 public void dynamicHeadWrite() { String fileName = TestFileUtil.getPath() + "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx"; EasyExcel.write(fileName) // 这里放入动态头 .head(head()).sheet("模板") // 当然这里数据也可以用 List<List> 去传入 .doWrite(data()); } 请问下,如何用这个支持国际化呢,没有什么思路,通过反射拿到所有@ExcelProperty字段的列名,然后挨个去国家化文件里找,最后将List<List<>>传给easyexcel,这样可行吗?或者有没有什么好的办法。 另外,这种明知自己的代码或者部署的环境有问题却不知道为什么的感觉真的太难受了,百思不得其解,为啥会过段时间就失效呢,太折磨了这种感觉

5

你可以把问题拆解开来,本质上就是需要根据条件拿到对应的表头信息。 国家参数---> 拿到对应的表头 用List<List<>>是可以的,如果是静态的表头 你甚至可以为国家写多个实体类。 举个例子


class ChineseHead{
@ExcelProperty("姓名")
 String name;
}

class EnglishHead{
@ExcelProperty("name")
String name;
}

然后把不同的ChineseHead.class 或者EnglishHead.class 当成传给导出内容

另外,这种明知自己的代码或者部署的环境有问题却不知道为什么的感觉真的太难受了,百思不得其解,为啥会过段时间就失效呢,太折磨了这种感觉

这句话我没太听懂 能具体点说吗

4
  • 注解在运行的时候他的生命周期不是跟着实例走的而是跟着class走的,也就是说就算你有不同的实例,但是他们都是同一个class那么你拿到的注解实例都是同一个。

  • 你贴出代码的操作我简单概括一下就是 在执行导入方法之前把注解中的value给改了,执行导入之后把值给写回来,那么在并发的情况下,可能在进入导入方法的时候被其他线程把值给改写回来了,那么你导出的数据自然是一个表达式了

  • 如果你执意要这么做,上锁可以解决你的问题

  • 想优雅一点可以自己拉个分支去改easyexcel,在读取head的时候去做个转义,可以在类 ExcelHeadProperty 去改写

  • 或者楼上的List<List> head 也是不错的选择

9

这个方法有线程隐患,但是不影响你这个功能 仍然不建议这么使用,建议用List<>的方式使用动态头

3

这个我明白,你们说的线程隐患我是理解了,我现在换了种做法,就是获取所有带有@ExcelProperty的字段,然后拿到里面的code去国际化文件查找,最后将得到的List<>传给easyexcel,这样的做法是不是没啥毛病?

另外就是想搞明白到底什么原因导致之前的代码会突然再也不起效果了:(

9

你这样是可以达到效果的,但是你把ExcelProperty注解值的语义变了 我的建议是新建一个注解,算是ExcelProperty的增强注解 你可以解析你自己的注解,这样既不影响原有功能 又有国际化的功能

1

明白,功能和我现在的描述一样,只是新建一个自定义注解,我尝试一下,看看还会不会出现之前失效的情况,理论上应该是没问题吧,毕竟是直接把表头传进去的

9

是的