ExportExcel-AnnotationList-反射

news/2025/11/18 13:53:36/文章来源:https://www.cnblogs.com/hanease/p/19237214

--------------------------------------------------------------------------------------------------------

这段代码用于通过自定义的 ExportExcel 工具类生成 Excel 文件并写入输出流,主要功能是导出案件信息数据。以下是代码的详细分析,包括逻辑流程、潜在问题及优化建议:

一、代码逻辑流程

  1. 初始化导出工具ExportExcel excel = new ExportExcel(lamCaseReportVOS.length);
    • 创建 ExportExcel 实例,参数为数据数组 lamCaseReportVOS 的长度(可能用于预分配资源或设置数据量)。
  2. 设置注解配置与创建工作表
    • excel.setAnnotationList(annotationListResult);:设置注解列表(推测用于 Excel 表头、字段映射等配置,如自定义列名、格式等)。
    • excel.addSheetAddSortNum("案件信息", LamCaseReportExportVO.class);:添加名为 “案件信息” 的工作表,并通过 LamCaseReportExportVO 类的结构生成表头(可能包含序号列,因方法名含 AddSortNum)。
  3. 工作表配置
    • Sheet sheet = excel.getSheet();:获取当前工作表对象。
    • sheet.createFreezePane(0, 1);:冻结首行(参数 0,1 表示冻结第 0 列前的列、第 1 行前的行,即首行固定),方便滚动时查看表头。
  4. 填充数据与写入输出流
    • excel.setDataList(Arrays.asList(lamCaseReportVOS), true);:将数组转换为列表并设置到 Excel 中(第二个参数 true 可能表示启用某种数据处理,如格式转换)。
    • wb = excel.getWb();:获取 Workbook 对象(POI 库的核心类,代表整个 Excel 文件)。
    • wb.write(outputStream);:将 Excel 内容写入输出流(可能是网络响应流或文件流)。
  5. 资源释放与日志记录
    • excel.dispose();:释放 ExportExcel 内部资源。
    • outputStream.flush();:刷新输出流,确保数据全部写出。
    • wb.close();:关闭 Workbook,释放 POI 占用的资源(如文件句柄)。
    • 记录导出总耗时(通过 stopWatch)。

二、潜在问题与风险

  1. 输出流关闭问题
    • 代码中仅调用 outputStream.flush(),但未显式关闭 outputStream。若 outputStream 是网络响应流(如 HttpServletResponse.getOutputStream()),可能导致连接泄露;若为文件流,可能导致文件损坏或占用磁盘资源。
    • 建议:在 finally 中关闭输出流(需判断非空):
      java
       
      运行
       
       
       
       
      if (outputStream != null) {try {outputStream.close();} catch (IOException e) {log.error("输出流关闭失败", e);}
      }
      
       
       
  2. 异常处理缺失
    • 核心逻辑(如 wb.write(outputStream))可能抛出 IOException,但代码未捕获,可能导致异常向上传播时未被处理,影响调用方(如 Web 场景下返回 500 错误但无详细日志)。
    • 建议:在 try 块中捕获异常并记录,或在方法上声明 throws IOException 由上层处理,避免静默失败。
  3. Workbook 关闭顺序
    • 代码中先 wb.close() 再处理其他资源,但 Workbook.close() 可能内部已包含流的关闭(如 POI 的 XSSFWorkbook),若 outputStream 依赖 Workbook 状态,可能导致数据未完全写出。
    • 建议:确保 wb.write(outputStream) 完成后,先刷新流,再关闭 Workbook,最后关闭输出流(顺序:write → flush → wb.close() → outputStream.close())。
  4. 大数量导出的内存风险
    • 若 lamCaseReportVOS 数据量过大(如十万级以上),Arrays.asList(lamCaseReportVOS) 会将所有数据加载到内存,结合 POI 的普通写入模式(非 SXSSF),可能导致 OOM(内存溢出)。
    • 判断依据:若 ExportExcel 内部使用 HSSFWorkbook.xls)或普通 XSSFWorkbook.xlsx),则存在此风险;若使用 SXSSFWorkbook(POI 的低内存模式),则风险较低。
    • 建议:确认 ExportExcel 是否支持大数据量导出,若不支持,需分批次写入或切换为 SXSSFWorkbook
  5. 变量 legCaseReport = null 无实际意义
    • 局部变量在方法结束后会自动回收,手动赋值 null 对垃圾回收无明显作用,可删除。

三、优化建议

  1. 完善资源关闭逻辑确保所有可关闭资源(WorkbookOutputStream)在 finally 中正确关闭,避免泄露。
  2. 添加异常日志在 try-catch 中记录关键步骤的异常(如写入失败、资源关闭失败),方便排查问题:
    java
     
    运行
     
     
     
     
    try {// 核心逻辑
    } catch (IOException e) {log.error("Excel导出失败", e);throw new RuntimeException("导出失败", e); // 根据业务需要决定是否抛出
    } finally {// 资源释放
    }
    
     
     
  3. 验证数据非空若 lamCaseReportVOS 为 nulllamCaseReportVOS.length 会抛 NullPointerException,建议先判断:
    java
     
    运行
     
     
     
     
    if (lamCaseReportVOS == null) {lamCaseReportVOS = new LamCaseReportExportVO[0]; // 空数组处理
    }
    ExportExcel excel = new ExportExcel(lamCaseReportVOS.length);
    
     
     
  4. 确认 Excel 格式与版本根据 Workbook 类型判断导出的是 .xls(HSSF,最大 65536 行)还是 .xlsx(XSSF/SXSSF,支持百万级行),避免数据量超过格式限制导致失败。
  5. 监控大文件导出性能若导出文件较大,stopWatch 记录的总耗时可用于性能分析,若耗时过长,可考虑异步导出(返回下载链接,后台异步生成文件)。

四、总结

这段代码实现了 Excel 导出的核心逻辑,但在资源管理和异常处理上存在优化空间。主要需关注:
  • 确保 OutputStream 和 Workbook 正确关闭;
  • 处理可能的异常并记录日志;
  • 评估数据量,避免大文件导出导致的内存问题。
     
    优化后可提高代码的健壮性和可维护性。

--------------------------------------------------------------------------------------------------------

这段代码的主要功能是根据输入的列名字符串(columns),从指定类(LamCaseReportExportVO)的 Excel 注解配置中筛选并排序对应的列信息,最终返回筛选后的列配置列表。常用于 Excel 导出时,根据前端指定的列名动态调整导出的列及其顺序。

代码逐行解析

1. 方法定义与参数校验

java
 
运行
 
 
 
 
private List<Object[]> getColumnsNew(String columns) throws NoSuchFieldException, IllegalAccessException {if (StringUtils.isBlank(columns)) {throw new BusinessException("导出列数据不能为空");}// ... 后续逻辑
}
 
  • 功能:定义一个私有方法,接收字符串参数columns(格式如"name,age,gender"),返回List<Object[]>(存储筛选后的列配置)。
  • 参数校验:若columns为空或空白,抛出业务异常(防止后续处理出错)。

2. 获取原始注解配置列表

java
 
运行
 
 
 
 
List<Object[]> annotationList = ExcelUtil.getAnnotationList(LamCaseReportExportVO.class);
 
  • 功能:通过工具类ExcelUtil获取LamCaseReportExportVO类中所有带 Excel 导出注解(如@ExcelField)的字段配置。
  • 返回值说明:annotationList是一个列表,每个元素Object[]存储单个字段的注解信息,推测结构为:
    • objects[0]:注解对象(如ExcelField实例,包含列名、排序、格式等配置)。
    • objects[1]:字段对应的 “标识值”(可能是字段名或注解中定义的唯一标识,用于匹配columns参数)。

3. 初始化结果列表与解析输入列

java
 
运行
 
 
 
 
List<Object[]> annotationListResult = new ArrayList<>();
if (StringUtils.isNotEmpty(columns)) {String[] split = columns.split(","); // 按逗号分割输入的列名,得到需要导出的列数组// ... 循环处理每一列
}
 
  • 功能:初始化结果列表annotationListResult,并将输入的columns按逗号分割为数组split(如"name,age"分割为["name", "age"])。

4. 筛选并调整列顺序

java
 
运行
 
 
 
 
for (int i = 0; i < split.length; i++) { // 遍历输入的每一列(i为目标顺序索引)for (Object[] objects : annotationList) { // 遍历原始注解配置中的每一列// 校验原始注解配置的有效性(确保有注解对象和标识值)if (objects.length > 1 && objects[0] != null && objects[1] != null) {ExcelField excelField = ((ExcelField) objects[0]); // 获取注解对象String value = objects[1].toString(); // 获取字段标识值(用于匹配)// 若输入的列标识与原始配置中的标识匹配if (split[i].equals(value)) {// 以下代码用于动态修改注解的sort属性(调整列的显示顺序)InvocationHandler invocationHandler = Proxy.getInvocationHandler(excelField);Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");memberValues.setAccessible(true); // 突破私有访问限制Map<String, Integer> memberValuesMap = (Map) memberValues.get(invocationHandler);memberValuesMap.put("sort", i); // 将当前列的排序值设为循环索引i(保证顺序与输入一致)annotationListResult.add(objects); // 将匹配的列配置加入结果列表}}}
}
 
  • 核心逻辑:
    1. 外层循环遍历输入的列(split数组),i表示该列在最终导出结果中的顺序(从 0 开始)。
    2. 内层循环遍历原始注解配置(annotationList),寻找与当前输入列(split[i])匹配的字段。
    3. 匹配条件:原始配置中的字段标识值(objects[1])与输入列名(split[i])相等。
    4. 动态调整排序:通过反射修改ExcelField注解的sort属性(排序值)为i,确保导出时列的顺序与输入columns的顺序一致。
    5. 将匹配的列配置加入结果列表annotationListResult

5. 返回结果

java
 
运行
 
 
 
 
return annotationListResult;
 
  • 返回筛选并排序后的列配置列表,供后续 Excel 导出时使用(如生成表头、填充数据)。

关键技术点:动态修改注解属性

注解在 Java 中默认是 “不可变的”,但代码中通过反射操作代理对象实现了动态修改ExcelFieldsort属性:
  1. Proxy.getInvocationHandler(excelField):获取注解对象的代理处理器(注解本质是通过动态代理实现的)。
  2. memberValues字段:代理处理器中存储注解属性键值对的 Map(私有字段,需通过setAccessible(true)突破访问限制)。
  3. memberValuesMap.put("sort", i):修改sort属性的值为当前列的顺序索引i,确保导出时按输入顺序排列。

功能总结

  • 作用:根据输入的列名字符串(columns),从LamCaseReportExportVO类的 Excel 注解中筛选出需要导出的列,并按输入顺序调整列的排序。
  • 应用场景:支持 Excel 导出时的 “动态列选择”,用户可指定需要导出的列及其显示顺序,增强导出功能的灵活性。
  • 注意点:依赖ExcelUtil.getAnnotationList方法正确解析类中的 Excel 注解,且columns参数的列名需与注解中的标识值一致(否则会筛选不到对应列)。

--------------------------------------------------------------------------------------------------------

问题的核心在于:多个线程并发调用时,通过反射修改了ExcelField注解的sort属性,导致LamCaseReportExportVO.class中注解的原始sort值被污染(因为注解实例在 JVM 中是单例的)。
解决思路是:避免直接修改原始注解对象的属性,而是为每个线程创建注解的副本(或新实例),在副本上修改sort值,确保原始注解的初始属性不受影响。

具体修改方案

步骤 1:新增 “复制注解” 的工具方法

通过动态代理创建ExcelField注解的副本,保留原始属性并允许修改,避免污染原始注解。
java
 
运行
 
 
 
 
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;/*** 复制注解的工具类,避免修改原始注解*/
public class AnnotationCopyUtil {/*** 复制ExcelField注解,返回新的代理实例*/public static ExcelField copyExcelField(ExcelField original) {if (original == null) {return null;}// 获取原始注解的属性值Map<String, Object> originalValues = getAnnotationValues(original);// 创建新的代理实例,代理ExcelField接口return (ExcelField) Proxy.newProxyInstance(ExcelField.class.getClassLoader(),new Class[]{ExcelField.class},new AnnotationInvocationHandler(ExcelField.class, originalValues));}/*** 获取注解的所有属性值(通过反射)*/private static Map<String, Object> getAnnotationValues(Annotation annotation) {try {InvocationHandler handler = Proxy.getInvocationHandler(annotation);java.lang.reflect.Field memberValuesField = handler.getClass().getDeclaredField("memberValues");memberValuesField.setAccessible(true);return new HashMap<>((Map<String, Object>) memberValuesField.get(handler));} catch (Exception e) {throw new RuntimeException("获取注解属性失败", e);}}/*** 自定义代理处理器,用于处理新注解的方法调用*/private static class AnnotationInvocationHandler implements InvocationHandler {private final Class<? extends Annotation> annotationType;private final Map<String, Object> values;public AnnotationInvocationHandler(Class<? extends Annotation> annotationType, Map<String, Object> values) {this.annotationType = annotationType;this.values = new HashMap<>(values); // 用新的Map存储属性,避免修改原始数据}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();if (methodName.equals("annotationType")) {return annotationType;}if (methodName.equals("toString")) {return annotationType.getName() + values.toString();}if (methodName.equals("hashCode")) {return calculateHashCode();}if (methodName.equals("equals")) {return proxy == args[0];}// 其他方法(属性getter)返回对应的值return values.get(methodName);}private int calculateHashCode() {int hashCode = 0;for (Map.Entry<String, Object> entry : values.entrySet()) {hashCode += 127 * entry.getKey().hashCode() ^ entry.getValue().hashCode();}return hashCode;}}
}
 

步骤 2:修改原方法,使用副本注解修改sort

核心改动:对从annotationList中获取的ExcelField注解,先复制一个副本,再修改副本的sort值,避免影响原始注解。
java
 
运行
 
 
 
 
private List<Object[]> getColumnsNew(String columns) throws NoSuchFieldException, IllegalAccessException {if (StringUtils.isBlank(columns)) {throw new BusinessException("导出列数据不能为空");}// 1. 获取原始注解列表(此时sort为初始值)List<Object[]> annotationList = ExcelUtil.getAnnotationList(LamCaseReportExportVO.class);List<Object[]> annotationListResult = new ArrayList<>();if (StringUtils.isNotEmpty(columns)) {String[] split = columns.split(",");for (int i = 0; i < split.length; i++) {for (Object[] objects : annotationList) {if (objects.length > 1 && objects[0] != null && objects[1] != null) {// 2. 关键:复制原始注解,得到一个新的副本(避免修改原始注解)ExcelField originalExcelField = (ExcelField) objects[0];ExcelField copiedExcelField = AnnotationCopyUtil.copyExcelField(originalExcelField);String value = objects[1].toString();if (split[i].equals(value)) {// 3. 修改副本注解的sort值(不影响原始注解)InvocationHandler invocationHandler = Proxy.getInvocationHandler(copiedExcelField);Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");memberValues.setAccessible(true);Map<String, Object> memberValuesMap = (Map) memberValues.get(invocationHandler);memberValuesMap.put("sort", i); // 此处修改的是副本的sort// 4. 将副本注解放入新的Object数组,加入结果列表Object[] copiedObjects = new Object[]{copiedExcelField, objects[1]}; // 保留原始标识值annotationListResult.add(copiedObjects);}}}}}return annotationListResult;
}
 

关键原理说明

  1. 注解的单例特性:Java 中,同一个注解在类加载后是单例的(全局唯一实例)。直接修改其属性会导致所有线程看到的sort值都被改变,引发并发污染。
  2. 副本隔离:通过AnnotationCopyUtil为每个需要修改的注解创建副本(新的代理实例),每个副本的sort值修改仅影响当前线程的当前操作,原始注解的sort值始终保持初始状态。
  3. 线程安全:副本注解是线程私有(每个线程操作自己的副本),避免了多线程并发修改的冲突,保证原始类中注解的初始sort值不被改变。

额外建议

  • ExcelUtil.getAnnotationList方法内部缓存了注解列表,建议确保缓存的是原始注解(未被修改的),避免缓存被污染。
  • 可在测试环境模拟多线程并发调用该方法,通过日志打印原始注解的sort值,验证是否已解决污染问题。
通过以上修改,既能满足动态调整sort值的需求,又能保证并发场景下原始注解属性的一致性。

--------------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------------

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/968919.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【重磅升级!迅为iTOP-Hi3403开发板SDK全面升级至Linux 6.6内核】

【重磅升级!迅为iTOP-Hi3403开发板SDK全面升级至Linux 6.6内核】重磅升级!迅为iTOP-Hi3403开发板SDK全面升级至Linux 6.6内核各位开发者朋友们,大家好! 亲爱的开发者们,好消息来啦!迅为电子始终走在技术前沿,现…

2025留学美国被开除怎么办?申诉挽回/学业急救/身份保留/转学规划/签证补救机构哪家强

2025留学美国被开除怎么办?申诉挽回/学业急救/身份保留/转学规划/签证补救机构哪家强随着赴美留学人数的持续攀升,学术诚信审核严格、GPA不达标、出勤异常等问题导致的留学生被开除情况也日益增多。美国留学被开除不…

2025年国内档案馆展示柜厂家综合实力排行榜TOP10

摘要 随着文博行业的快速发展,档案馆展示柜作为文物保护和展示的重要载体,其市场需求持续增长。2025年,行业内涌现出一批技术实力雄厚、服务质量优秀的厂家。本文基于市场调研和用户反馈,为您精选十家值得信赖的档…

2025年陕西省探矿权采矿权技术服务企业权威推荐榜单

摘要 随着矿产资源管理的日益规范,陕西省探矿权采矿权技术服务行业在2025年迎来了快速发展,专业服务机构在土地审批、矿产开发等领域发挥关键作用。本文基于行业数据和用户口碑,综合评测排名前十的服务提供商,为有…

WPS用Qt还情有可原

百度网盘APP应该用本地HTTPS服务器+Web么。 本地HTTPS服务器里可以爱干啥干啥么。/opt/baidunetdisk$ l 总计 230M -rwxr-xr-x 1 root root 128M 2023年 1月19日 baidunetdisk -rw-rw-r-- 1 root root 1.9K 2023年 1月…

2025年山西口碑好的纪念馆展示柜厂家十大排名权威推荐

摘要 随着文博产业的快速发展,纪念馆展示柜行业迎来新一轮技术升级与市场需求增长。2025年,山西地区纪念馆展示柜厂家在技术创新、工艺品质和服务体系等方面展现出强劲竞争力。本文基于行业数据、用户口碑和技术参数…

2025年评价高的UV 软膜广告灯箱厂家最新TOP排行

2025年评价高的UV软膜广告灯箱厂家最新TOP实力排行行业背景与市场趋势UV软膜广告灯箱作为现代商业展示的重要载体,近年来在户外广告、商场导视、品牌展示等领域应用广泛。根据《2024-2025年中国广告标识行业白皮书》数…

2025年山西口碑好的纪念馆展示柜定制厂家排行Top10推荐

摘要 纪念馆展示柜行业在2025年迎来快速发展,随着文化强国战略的推进,山西及国内文博场馆对高品质展示柜需求激增。本文基于行业数据与用户口碑,精选排名前十的厂家,为采购决策提供参考。摘要部分包含行业发展概述…

C# 14 新功能全面解析:提升生产力与性能的革命性更新

引言 C# 语言作为.NET生态的核心,始终致力于提升开发者的生产力与应用程序性能。C# 14带来了多项突破性特性,包括扩展成员、字段关键词、空条件赋值等,这些改进显著减少了样板代码,增强了类型系统的灵活性。本文将…

【隐语SecretFlow隐私计算】如何使用 Kuscia API 运行一个 SecretFlow Serving

打开链接点亮社区Star,照亮技术的前进之路。每一个点赞,都是社区技术大佬前进的动力Github 地址: https://github.com/orgs/secretflow/repositories?q=sort%3Astars本教程将以 SecretFlow Serving 内置测试模型为…

2025年优秀的软件行业体系认证三体系认证品牌实力推荐榜

2025年优秀的软件行业体系认证三体系认证品牌实力推荐榜行业背景与市场趋势随着数字化转型浪潮席卷全球,软件行业作为数字经济的基础设施,其质量管理和标准化建设日益受到重视。根据国际标准化组织(ISO)最新发布的《…

2025年11月道德经讲师推荐榜单:五位讲师详细对比与评价

作为一位希望深入学习《道德经》的读者或企业培训负责人,您可能正在寻找一位能够将古老智慧与现代实践相结合的讲师。无论是用于个人修养提升、企业内部文化建设,还是特定行业协会的专题讲座,选择一位合适的讲师至关…

linux ajax

您提到的“Linux Ajax”可能是一个术语或概念的混淆,因为 Ajax 是一种前端技术(Javascript 的异步请求库),与 Linux 操作系统本身并无直接关联。 如果您是想了解 Linux 中的 Ajax 技术 或 Linux 与 Ajax 的结合使用…

linux adobe reader

Adobe Reader 是一款由 Adobe 开发的 PDF 阅读器,主要用于查看、注释、打印和编辑 PDF 文件。在 Linux 系统中,Adobe Reader 不是原生安装的,需要通过第三方软件包或从 Adobe 官方下载安装。 一、在 Linux 上安装 A…

2025年11月中国香菇品牌排名

摘要 香菇行业作为健康食品领域的重要组成部分,近年来随着消费者对食品安全和品质要求的提升,呈现出快速增长的趋势。2025年,中国香菇市场预计规模将突破500亿元,年复合增长率达8%,主要驱动因素包括健康饮食意识增…

2025年11月中国枸杞企业口碑推荐榜单

摘要 2025年,中国枸杞行业持续蓬勃发展,市场规模预计突破500亿元,消费者对高品质、安全枸杞的需求日益增长。本榜单基于行业数据、客户反馈和权威评测,为您推荐口碑领先的枸杞企业,并提供详细对比,助您快速选择优…

2025留学开除申诉机构推荐Top5,学术诚信申诉/退学申诉全流程护航

2025留学开除申诉机构推荐Top5,学术诚信申诉/退学申诉全流程护航随着留学群体规模持续扩大,学术诚信审查、出勤考核等标准日益严格,留学生面临的开除、停学等风险显著增加,专业开除申诉机构的市场需求呈爆发式增长…

机器视觉:智能车大赛视觉组手艺文档——用 YOLO3 Nano 实现目标检测并部署到 OpenART

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

C#重要的数据结构

ConcurrentDictionary 线程安全,内置同步机制支持多线程并发读写SortedDictionary