当前位置: 首页 > news >正文

自定义注解实现Excel 导出

概述

一个用自定义注解实现导出字段定义的工具实现。

1. 注解定义,定义导出Excel的字段

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PoiExportField {// Label of the columnString label();// Order of the column,default 0,means the first columnint order() default 0;// If true, this field will be used to create subgroup rowsboolean subGroup() default false;// Width of the columnint width() default 20;// Alignment of the columnHorizontalAlignment align() default HorizontalAlignment.LEFT;
}

2. 实体类,使用注解定义导出字段,不导出的字段不用加注解

@Data
public class OrderVO {@PoiExportUtil.PoiExportField(label = "订单编号", order = 1, align = HorizontalAlignment.CENTER)private String orderNo;@PoiExportUtil.PoiExportField(label = "订单用户", order = 2, align = HorizontalAlignment.CENTER)private String orderUser;@PoiExportUtil.PoiExportField(label = "订单时间", order = 3, align = HorizontalAlignment.CENTER)private String orderTime;@PoiExportUtil.PoiExportField(label = "订单金额", order = 4, width = 15, align = HorizontalAlignment.RIGHT)private String orderAmount;private String orderDesc;private String orderRemark;private String orderPhone;private String orderZipCode;@PoiExportUtil.PoiExportField(label = "订单国家", subGroup = true)private String orderCountry;@PoiExportUtil.PoiExportField(label = "订单省份", subGroup = true)private String orderProvince;@PoiExportUtil.PoiExportField(label = "订单城市", order = 6)private String orderCity;@PoiExportUtil.PoiExportField(label = "详细地址", order = 7)private String orderAddressDetail;}

3. Excel导出工具类,注解定义放到工具类中,方便使用

public class PoiExportUtil {/*** Custom annotation for exporting Excel file*/@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface PoiExportField {// Label of the columnString label();// Order of the column,default 0,means the first columnint order() default 0;// If true, this field will be used to create subgroup rowsboolean subGroup() default false;// Width of the columnint width() default 20;// Alignment of the columnHorizontalAlignment align() default HorizontalAlignment.LEFT;}/*** Export data to excel file** @param list     List of data* @param fileName File name* @param <T>      Type of data*/public <T> void exportToExcel(List<T> list, String fileName) {Workbook workbook = new XSSFWorkbook();Sheet sheet = workbook.createSheet("Data");writeSheet(sheet, list, null, null, null);// Write to filetry (FileOutputStream fileOut = new FileOutputStream(fileName)) {workbook.write(fileOut);fileOut.flush();} catch (Exception e) {e.printStackTrace();}}/*** Write data to a sheet** @param sheet         Sheet* @param dataList      List of data* @param headerStyle   Header style* @param subGroupStyle Subgroup style* @param dataCellStyle Data cell style* @param <T>           Type of data*/public <T> void writeSheet(Sheet sheet, List<T> dataList, CellStyle headerStyle, CellStyle subGroupStyle, CellStyle dataCellStyle) {// If data list is empty, returnif (dataList == null || dataList.isEmpty()) {return;}// If styles are not provided, use default stylesif (headerStyle == null) {headerStyle = createDefaultHeaderStyle(sheet.getWorkbook());}if (subGroupStyle == null) {subGroupStyle = createDefaultSubGroupStyle(sheet.getWorkbook());}if (dataCellStyle == null) {dataCellStyle = createDefaultDataCellStyle(sheet.getWorkbook());}Field[] fields = dataList.get(0).getClass().getDeclaredFields();// Filter fields with PoiExportField annotationList<Field> annotatedFields = new ArrayList<>();// Filter fields with PoiExportField annotation and subGroup is true, for creating subgroup rowsList<Field> subGroupFields = new ArrayList<>();// Filter fields with PoiExportField annotation and sort them by order attributefor (Field field : fields) {PoiExportField annotation = field.getAnnotation(PoiExportField.class);if (annotation != null) {if (annotation.subGroup()) {subGroupFields.add(field);} else {annotatedFields.add(field);}}}// Sort fields by order attributeannotatedFields.sort(Comparator.comparingInt(field -> {PoiExportField annotation = field.getAnnotation(PoiExportField.class);return annotation.order();}));//annotated fields is empty, returnif (annotatedFields.isEmpty()) {return;}// Create header rowcreateHeaderRow(sheet, annotatedFields, headerStyle);// Create data rowscreateSheetWithData(sheet, dataList, annotatedFields, subGroupFields, subGroupStyle, dataCellStyle);}/*** Create header row** @param sheet           Sheet* @param annotatedFields List of annotated fields* @param headerStyle     Header style*/private void createHeaderRow(Sheet sheet, List<Field> annotatedFields, CellStyle headerStyle) {int lastRowNum = sheet.getLastRowNum();Row headerRow = sheet.createRow(lastRowNum + 1);for (int i = 0; i < annotatedFields.size(); i++) {Field field = annotatedFields.get(i);PoiExportField annotation = field.getAnnotation(PoiExportField.class);Cell headerCell = headerRow.createCell(i);headerCell.setCellValue(annotation.label());headerCell.setCellStyle(headerStyle);// Set column widthsheet.setColumnWidth(i, annotation.width() * 256);}}/*** Create data rows** @param sheet           Sheet* @param dataList        List of data* @param annotatedFields List of annotated fields* @param subGroupFields  List of subgroup fields* @param subGroupStyle   Subgroup style* @param dataCellStyle   Data cell style* @param <T>             Type of data*/private <T> void createSheetWithData(Sheet sheet, List<T> dataList, List<Field> annotatedFields, List<Field> subGroupFields, CellStyle subGroupStyle, CellStyle dataCellStyle) {String lastSubGroupValue = null;int rowIndex = sheet.getLastRowNum() + 1;for (T data : dataList) {// Create subgroup rowif (subGroupFields != null && !subGroupFields.isEmpty()) {String currentSubGroupValue = getSubGroupValue(data, subGroupFields);if (!currentSubGroupValue.equals(lastSubGroupValue)) {Row subGroupRow = sheet.createRow(rowIndex++);Cell subGroupCell = subGroupRow.createCell(0);subGroupCell.setCellValue(currentSubGroupValue);CellRangeAddress mergeRegion = new CellRangeAddress(rowIndex - 1, rowIndex - 1, 0, annotatedFields.size() - 1);sheet.addMergedRegion(mergeRegion);//CellRangeAddress  mergeRegion = sheet.getMergedRegion(sheet.getNumMergedRegions() - 1);RegionUtil.setBorderBottom(BorderStyle.THIN, mergeRegion, sheet);RegionUtil.setBorderTop(BorderStyle.THIN, mergeRegion, sheet);RegionUtil.setBorderLeft(BorderStyle.THIN, mergeRegion, sheet);RegionUtil.setBorderRight(BorderStyle.THIN, mergeRegion, sheet);subGroupCell.setCellStyle(subGroupStyle);lastSubGroupValue = currentSubGroupValue;}}// Create data rowRow row = sheet.createRow(rowIndex++);for (int j = 0; j < annotatedFields.size(); j++) {Field field = annotatedFields.get(j);PoiExportField annotation = field.getAnnotation(PoiExportField.class);Cell cell = row.createCell(j);try {// Get field value from getter methodfield.setAccessible(true);String getterName = "get" + Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);Method getterMethod = data.getClass().getMethod(getterName);Object value = getterMethod.invoke(data);if (value != null) {cell.setCellValue(value.toString());}// Set cell style alignmentCellStyle cellStyle = sheet.getWorkbook().createCellStyle();cellStyle.cloneStyleFrom(dataCellStyle);cellStyle.setAlignment(annotation.align());cell.setCellStyle(cellStyle);} catch (Exception e) {e.printStackTrace();}}}}/*** Get subgroup value** @param data           Data* @param subGroupFields List of subgroup fields* @param <T>            Type of data* @return Subgroup value*/private <T> String getSubGroupValue(T data, List<Field> subGroupFields) {StringBuilder subGroupValue = new StringBuilder();for (Field field : subGroupFields) {try {field.setAccessible(true);Object value = field.get(data);if (value != null) {subGroupValue.append(value.toString()).append(" ");}} catch (IllegalAccessException e) {e.printStackTrace();}}return subGroupValue.toString().trim();}/*** Create and return default header style** @param workbook Workbook* @return CellStyle*/private CellStyle createDefaultHeaderStyle(Workbook workbook) {Font fontBold = workbook.createFont();fontBold.setBold(true);fontBold.setFontHeightInPoints((short) 12);CellStyle headerStyle = workbook.createCellStyle();headerStyle.setBorderTop(BorderStyle.THIN);headerStyle.setBorderBottom(BorderStyle.THIN);headerStyle.setBorderLeft(BorderStyle.THIN);headerStyle.setBorderRight(BorderStyle.THIN);headerStyle.setAlignment(HorizontalAlignment.CENTER);headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);headerStyle.setWrapText(true);headerStyle.setFont(fontBold);return headerStyle;}/*** Create and return default subgroup style** @param workbook Workbook* @return CellStyle*/private CellStyle createDefaultSubGroupStyle(Workbook workbook) {// Create and return default subgroup styleFont fontBold = workbook.createFont();fontBold.setBold(true);fontBold.setFontHeightInPoints((short) 11);CellStyle subGroupStyle = workbook.createCellStyle();subGroupStyle.setBorderTop(BorderStyle.THIN);subGroupStyle.setBorderBottom(BorderStyle.THIN);subGroupStyle.setBorderLeft(BorderStyle.THIN);subGroupStyle.setBorderRight(BorderStyle.THIN);subGroupStyle.setAlignment(HorizontalAlignment.CENTER);subGroupStyle.setVerticalAlignment(VerticalAlignment.CENTER);subGroupStyle.setFont(fontBold);return subGroupStyle;}/*** Create and return default data cell style** @param workbook Workbook* @return CellStyle*/private CellStyle createDefaultDataCellStyle(Workbook workbook) {// Create and return default data cell styleCellStyle dataCellStyle = workbook.createCellStyle();Font fontBold = workbook.createFont();fontBold.setBold(true);fontBold.setFontHeightInPoints((short) 11);dataCellStyle.setBorderTop(BorderStyle.THIN);dataCellStyle.setBorderBottom(BorderStyle.THIN);dataCellStyle.setBorderLeft(BorderStyle.THIN);dataCellStyle.setBorderRight(BorderStyle.THIN);dataCellStyle.setFont(fontBold);return dataCellStyle;}
}

4. 测试

public class PoiExportUtilTest {@Testpublic void exportToExcel() {PoiExportUtil poiExportUtil = new PoiExportUtil();List<OrderVO> orderVOList = generateOrders();poiExportUtil.exportToExcel(orderVOList, "order.xlsx");}public List<OrderVO> generateOrders() {String[] COUNTRIES = {"China", "Japan", "Canada"};Random RANDOM = new Random();List<OrderVO> orders = new ArrayList<>();for (int i = 0; i < 30; i++) {String orderNo = "OrderNo" + (i + 1);String orderUser = "User" + (i + 1);String orderTime = "Time" + (i + 1);String orderAmount = RANDOM.nextInt(10000) + ".00";String orderDesc = "Desc" + (i + 1);String orderRemark = "Remark" + (i + 1);String orderPhone = "Phone" + (i + 1);String orderZipCode = "ZipCode" + (i + 1);String orderCountry = COUNTRIES[RANDOM.nextInt(COUNTRIES.length)];String orderProvince = "Province" + (i + 1) % 3;String orderCity = "City" + (i + 1);String orderAddressDetail = "AddressDetail" + (i + 1);OrderVO order = createOrder(orderNo, orderUser, orderTime, orderAmount, orderDesc, orderRemark, orderPhone,orderZipCode, orderCountry, orderProvince, orderCity, orderAddressDetail);orders.add(order);}// Sort by orderCountry and orderTimereturn orders.stream().sorted(Comparator.comparing(OrderVO::getOrderCountry).thenComparing(OrderVO::getOrderProvince).thenComparing(OrderVO::getOrderTime)).collect(Collectors.toList());}private OrderVO createOrder(String orderNo, String orderUser, String orderTime, String orderAmount,String orderDesc, String orderRemark, String orderPhone, String orderZipCode,String orderCountry, String orderProvince, String orderCity, String orderAddressDetail) {OrderVO order = new OrderVO();order.setOrderNo(orderNo);order.setOrderUser(orderUser);order.setOrderTime(orderTime);order.setOrderAmount(orderAmount);order.setOrderDesc(orderDesc);order.setOrderRemark(orderRemark);order.setOrderPhone(orderPhone);order.setOrderZipCode(orderZipCode);order.setOrderCountry(orderCountry);order.setOrderProvince(orderProvince);order.setOrderCity(orderCity);order.setOrderAddressDetail(orderAddressDetail);return order;}
}

参考源代码

相关文章:

自定义注解实现Excel 导出

概述 一个用自定义注解实现导出字段定义的工具实现。 1. 注解定义,定义导出Excel的字段 Target(ElementType.FIELD) Retention(RetentionPolicy.RUNTIME) public interface PoiExportField {// Label of the columnString label();// Order of the column,default 0,means t…...

先求生存,再谋发展:俞敏洪的创业哲学与产品创新之路

引言&#xff1a; 在创业的道路上&#xff0c;每一个创业者都面临着无数的挑战和选择。俞敏洪&#xff0c;新东方教育科技集团的创始人&#xff0c;以其独特的创业哲学和坚韧不拔的精神&#xff0c;带领新东方从一个小小的培训机构成长为全球知名的教育品牌。他的成功经验告诉…...

【Spark】直接从DataFrame的schema创建表

// 基于DataFrame创建表 def createTable(dataFrame: DataFrame,partitionColumns: Array[String],databaseName: String,tableName: String): Unit = {...

Decimal要从str转换以避免精度问题

最近遇到一个python的小数的问题&#xff0c;本来应该很简单的小于判断&#xff0c;无论如何都不正确&#xff0c;而且浮点小数都没问题&#xff0c;但decimal小数有问题&#xff0c;给我整蒙了&#xff0c;后来才发现是对decimal不了解所致&#xff0c;如果你还用float转decim…...

STM32项目分享:智能家居安防系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板及元器件图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.c…...

qt c++类继承QWidget和不继承有什么区别

class CheckBoxSetting {Q_OBJECT public:CheckBoxSetting(); };和 class CheckBoxSettingsEditor : public QWidget {Q_OBJECTpublic:explicit CheckBoxSettingsEditor(QWidget *parent 0);~CheckBoxSettingsEditor();有什么区别&#xff1f; 这两个类 CheckBoxSetting 和 C…...

什么是SIEM

SIEM 解决方案是一种企业级应用程序&#xff0c;可集中和自动化与网络安全相关的操作&#xff0c;该工具通过收集、分析和关联从组织 IT 基础设施中的各种实体聚合的网络事件来帮助应对网络威胁。 与帮助监控和评估组织物理空间中的危险的监视控制台相比&#xff0c;SIEM解决方…...

浅谈一下实例化

实例化对象是面向对象编程中非常重要的概念&#xff0c;它允许我们根据类的定义创建具体的对象&#xff0c;并操作这些对象的属性和方法。下面具体谈一下实例化对象的一些特点和用途&#xff1a; 封装性和复用性&#xff1a;实例化对象可以将数据和行为封装在一起&#xff0c;从…...

【人工智能】第三部分:ChatGPT的应用场景和挑战

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…...

FLV 文件格式

FLV 总体结构 FLV 文件由 FLV文件头(FLV Header)和 FLV文件体(FLV Body)组成。 FLV 文件体由若干级联的 FLV标签(FLV Tag)组成。标签使用一个 PreviousTagSize(uint32_t)来保存前一个 FLV 标签的大小,第一个 PreviousTagSize 值为0。 一个 FLV 文件中的所有数据,如 视频…...

FENDI CLUB精酿啤酒品鉴体验

当提及“品质卓越&#xff0c;口感非凡”的啤酒时&#xff0c;FENDI CLUB精酿啤酒无疑是一个值得一试的选择。这款啤酒以其独特的酿造工艺和优质的原料&#xff0c;为消费者带来了与众不同的味觉享受。 一、独特的酿造工艺 FENDI CLUB精酿啤酒在酿造过程中&#xff0c;严格遵循…...

前端 CSS 经典:水波进度样式

前言&#xff1a;简单实现水波进度样式&#xff0c;简单好看。 效果图&#xff1a; 代码实现&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8" /><meta http-equiv"X-UA-Compatible" cont…...

深入解析CSS中的块级元素

块级元素在CSS中是一种常见的元素类型&#xff0c;具有一些特定的表现和行为特征。了解块级元素的定义和特点对于掌握CSS布局和样式设计至关重要。本文将从多个角度深入解析CSS中的块级元素&#xff0c;探讨其含义、特点以及在页面布局中的应用。 什么是块级元素&#xff1f; …...

PDF裁剪网站

裁剪 PDF – 修剪 PDF 文件中不需要的空白...

数据结构复习指导之外部排序

目录 外部排序 复习提示 1.外部排序的基本概念 2.外部排序的方法 2.1对大文件排序时使用的排序算法&#xff08;2016&#xff09; 3.多路平衡归并与败者树 4.置换-选择排序&#xff08;生成初始归并段&#xff09; 4.1置换-选择排序生成初始归并段的实例(2023) 5.最佳…...

【Python报错】已解决TypeError: can only concatenate str (not “int“) to str

解决Python报错&#xff1a;TypeError: can only concatenate str (not “int”) to str 在Python中&#xff0c;字符串连接是常见的操作&#xff0c;但如果你尝试将整数&#xff08;int&#xff09;与字符串&#xff08;str&#xff09;直接连接&#xff0c;会遇到TypeError: …...

Log4j日志级别介绍

Log4j 是一个广泛使用的 Java 日志记录框架&#xff0c;提供了多种日志级别&#xff0c;用于控制日志输出的详细程度。每个日志级别代表一种特定的重要性和紧急程度。 以下是 Log4j 的常见日志级别及其解读&#xff1a; FATAL&#xff08;致命&#xff09; 解释&#xff1a;表…...

[MQTT]服务器EMQX搭建SSL/TLS连接过程(wss://)

&#x1f449;原文阅读 &#x1f4a1;章前提示 本文采用8084端口进行连接&#xff0c;是EMQX 默认提供了四个常用的监听器之一&#xff0c;如果需要添加其他类型的监听器&#xff0c;可参考官方文档&#x1f517;管理 | EMQX 文档。 本文使用自签名CA&#xff0c;需要提前在L…...

【纯血鸿蒙】——响应式布局如何实现?

前面介绍了自适应布局&#xff0c;但是将窗口尺寸变化较大时&#xff0c;仅仅依靠自适应布局可能出现图片异常放大或页面内容稀疏、留白过多等问题。此时就需要借助响应式布局能力调整页面结构。 响应式布局 响应式布局是指页面内的元素可以根据特定的特征&#xff08;如窗口…...

深入理解Django Serializer及其在Go语言中的实现20240604

深入理解Django Serializer及其在Go语言中的实现 在现代Web开发中&#xff0c;前后端分离已成为主流架构模式。作为开发者&#xff0c;我们经常需要处理数据的序列化和反序列化&#xff0c;以便在前后端之间传递数据。在Django中&#xff0c;Serializer是一个强大的工具&#…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...