首页 / 资讯中心 / 文章详情

Spring 项目中如何组织 JQuick-Excel 的 XML 与 Bean

Spring 项目中如何组织 JQuick-Excel 的 XML 与 Bean ★ FEATURED ARTICLE
Spring 项目中如何组织 JQuick-Excel 的 XML 与 Beantags: #JQuickExcel #JavaExcel #SpringBoot #开源 #POI #Excel工具更多示例: JQuick-Excel 官网示例 https://www.jquick.org/jquick-excel/#demo-formulas0005简介我是 JQuick-Excel 的作者。在 Spring 项目里接入 Excel最容易失控的地方往往不是控制器而是散落在业务代码里的字段映射、资源路径和流对象。本篇给出我实际建议的组织方式把 JQuick-Excel DSL 放进 resources把接口与 Spring Bean 分层管理并始终通过JQuickXmlFactory、导入/导出解析器执行规则。前言我不会把 JQuick-Excel 当成一个需要“注解扫描后自动生效”的 Spring 组件。根据 README-CN 和仓库测试核心调用链是明确的Java 准备JQuickRow、JContext与输入或输出流JQuickExcelExportXmlParseFactory或JQuickExcelImportXmlParseFactory保存本次执行所需资源JQuickXmlFactory从 classpath XML 创建接口代理代理再按方法名执行EXPORT WITH或IMPORT WITHDSL。因此我在 Spring 工程中把 Spring 的职责限定为依赖注入和 HTTP/任务调度把 Excel 规则留在 XML。这里没有使用、也没有假定存在任何 JQuick-Excel 的 Spring Boot starter、自动配置、Enable...注解或自动注册 Bean。即使项目本身采用 Spring BootJQuick-Excel 的核心仍由下面展示的工厂、解析器和 resources XML DSL 显式调用。一个实用的目录划分如下服务接口放在com.example.report.excelDSL 放在src/main/resources/excel/student-excel.xml负责组装运行时数据的 Spring Bean 放在service包。接口和 XML 是稳定契约业务 DTO、下载流、上传流则是每次请求不同的输入。环境与依赖本文以 Java 8、Spring Framework 或 Spring Boot Web 环境为前提。JQuick-Excel 使用 Maven 坐标io.github.paohaijiao:jquick-excel:3.6.0Apache POI 是它的项目依赖不需要为了本文另写一套 POI 读写代码。dependencygroupIdio.github.paohaijiao/groupIdartifactIdjquick-excel/artifactIdversion3.6.0/version/dependency将 XML 放入src/main/resources/excel/student-excel.xml。JQuickXmlFactory接收的是 classpath 资源名所以后续 Java 必须传入excel/student-excel.xml而不是开发机绝对路径。namespace必须等于接口全限定名excel/name必须等于接口方法名。?xml version1.0 encodingUTF-8?!DOCTYPEexcelsPUBLIC-//PAOHAIJIAO//DTD API EXCEL 1.0//ENclasspath:paohaijiao/dtd/Jquick-excel.dtdexcelsnamespacecom.example.report.excel.StudentExcelApiexcelnameexportStudentsreturnClassvoid![CDATA[ EXPORT WITH SHEET学生表, HEADERtrue, MAPPING{id:学号,name:姓名,gender:性别,enrollmentDate:入学日期}, TRANSFORM{ gender:trans(${dict},${gender}), enrollmentDate:dateFormat(${enrollmentDate},yyyy-MM-dd) } ]]/excelexcelnameimportStudentsreturnClassjava.util.List![CDATA[ IMPORT WITH SHEET学生表, HEADERtrue, MAPPING{学号:id,姓名:name,性别:gender,入学日期:enrollmentDate}, TRANSFORM{gender:trans(${dict},${gender})} ]]/excel/excels代码示例我先定义一个只表达 XML 代理契约的接口。README-CN 与测试均使用Param和ListJQuickRow下面没有杜撰 DTO 自动转换 API。packagecom.example.report.excel;importcom.github.paohaijiao.statement.JQuickRow;importcom.github.paohaijiao.xml.param.Param;importjava.util.List;publicinterfaceStudentExcelApi{voidexportStudents(Param(field)Stringfield,Param(value)Stringvalue);ListJQuickRowimportStudents(Param(field)Stringfield,Param(value)Stringvalue);}我把代理创建封装在 Spring 服务中但没有把解析器或代理声明为单例 Bean。它们携带当前请求的行数据和流复用会把不同请求的状态混在一起。导出每次新建解析器、工厂和 API然后立即调用。packagecom.example.report.service;importcom.example.report.excel.StudentExcelApi;importcom.github.paohaijiao.param.JContext;importcom.github.paohaijiao.statement.JQuickRow;importcom.github.paohaijiao.util.JObjectConverter;importcom.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;importcom.github.paohaijiao.xml.factory.JQuickFactory;importcom.github.paohaijiao.xml.factory.JQuickXmlFactory;importcom.github.paohaijiao.xml.handler.JQuickParseHandler;importorg.springframework.stereotype.Service;importjava.io.OutputStream;importjava.util.HashMap;importjava.util.List;importjava.util.Map;ServicepublicclassStudentExcelService{publicvoidexport(ListMapString,Objectstudents,OutputStreamoutput){ListJQuickRowrowsJQuickRow.toRows(JObjectConverter.convert(students));JContextcontextnewJContext();MapString,ObjectdictnewHashMap();dict.put(0,男);dict.put(1,女);context.put(dict,dict);JQuickParseHandlerparsernewJQuickExcelExportXmlParseFactory(context,rows,output);JQuickFactoryfactorynewJQuickXmlFactory(parser,excel/student-excel.xml);StudentExcelApiapifactory.createApi(StudentExcelApi.class);api.exportStudents(field,value);}}导入同样由同一个服务接收InputStream创建JQuickExcelImportXmlParseFactory(context, input)再调用api.importStudents。我通常让控制器或应用服务完成JQuickRow到领域对象的校验和保存避免把持久化逻辑塞进 XML。原理说明XML 中的MAPPING在导出时是“内部字段到表头”导入时是“表头到内部字段”。HEADERtrue表示生成或识别第一行表头。TRANSFORM中的${dict}来自JContext导出字典方向为编码到显示值导入应反过来例如“男”到“0”。同名dict可以存在但不能不经转换就复用同一张映射表。Spring Bean 的价值是把数据查询、字典准备和事务边界放在熟悉的应用层它不替代 XML DSL。每次执行仍由JQuickXmlFactory读取资源导入/导出解析器分别绑定数据流和文件流。这样报表字段、表头和转换变更集中在一个 resources 文件HTTP、定时任务和消息消费等入口则可以共用 Spring 服务。注意事项不要把OutputStream、InputStream、导出解析器或接口代理缓存为单例字段它们与单次文件处理绑定。excel/student-excel.xml必须被构建工具打进 classpath部署包中遗漏资源会导致工厂无法定位 XML。接口全限定名、namespace、方法名、returnClass必须同时对齐改包名时尤其容易漏改 XML。DSL 只使用 README-CN 和测试 XML 已出现的EXPORT WITH、IMPORT WITH、SHEET、HEADER、MAPPING、TRANSFORM与dateFormat、trans。本文未声称存在 JQuick-Excel Starter 或自动配置Spring 注解仅来自标准 Spring 框架。运行步骤与工程化组织我在一个新的 Spring 工程中会先建立稳定的资源契约再写 Controller。第一步将接口放入普通 Java 包将 XML 放入src/main/resources/excel并让namespace和接口全限定名一致。第二步写一个只接收ListMapString, Object与OutputStream的导出服务以及一个只接收InputStream的导入服务二者内部每次调用都创建新的JContext、解析器、JQuickXmlFactory和 API 代理。第三步用 JUnit 或现有集成测试生成一个最小 xlsx再以该文件反向导入验证工作表名、表头、字段映射和字典方向。只有该闭环通过后再把服务接到 HTTP、定时任务或消息消费入口。Bean 的边界比注解数量重要。数据查询、权限过滤、字典查询和事务由 Spring 服务负责XML 资源名、接口名、DSL 规则由 Excel 适配层负责Web Controller 只做请求解析、响应协议和调用编排。不要将InputStream、OutputStream、JQuickParseHandler、JQuickFactory或代理对象注入为单例字段它们绑定的是一次文件处理。可以注入稳定的查询服务和配置对象但应在方法体内根据当前请求的数据和流组装 JQuick-Excel 对象。多份报表的组织方式也应保持可扫描。我的惯例是一个 XML 对应一个接口资源名与报表语义一致例如excel/student-excel.xml和StudentExcelApi版本升级时只复制并改名规则文件不覆盖仍被旧接口使用的模板。公共字典键可以统一约定例如dict但每个操作仍要明确编码到显示值还是显示值到编码。对于租户、语言或日期格式等运行期差异应在 Spring 服务层准备JContext不要通过修改 classpath XML 来承载每个请求的动态状态。构建与部署时我会检查最终 jar 是否包含excel/*.xml以及 DTD 所需的 classpath 资源。IDE 直接运行成功并不能证明打包成功资源过滤、重命名或模块间依赖范围都可能让部署包缺少 XML。发布前至少执行一次打包后的启动测试并调用导出、导入各一次。出现“代理找不到规则”时先看资源名、接口包名和 XML 的excel/name不要立即把问题归因给 Spring Bean 扫描因为本方案本来就不依赖 JQuick-Excel 自动扫描。在事务上我把文件解析和持久化分开考虑。导入得到ListJQuickRow后应用层应先完成字段校验、重复数据判断和错误聚合再决定是否进入写库事务不要在读取到每一行时立即提交不可逆业务操作。导出则先完成权限与数据范围校验再取得响应流并开始写入HTTP 响应开始提交后无法可靠替换成结构化错误 JSON因此耗时查询和可预见的校验应尽量前置。以上都属于标准 Spring 应用组织并不要求任何 JQuick-Excel Starter 或自动配置。总结我在 Spring 中采用“XML 管规则、Bean 管运行时资源”的方式resources XML 定义导入导出 DSL接口定义代理契约Spring 服务在每次调用时创建JQuickXmlFactory与对应解析器。这样既保留 Spring 的组织能力也不掩盖 JQuick-Excel 真正的执行入口后续接入下载、上传或批量任务时都能复用同一套规则。
阅读完成 · 觉得有帮助?
咨询建站