博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
自定义注解通过反射实现Excel的导出功能(提供项目源码)
阅读量:3945 次
发布时间:2019-05-24

本文共 16042 字,大约阅读时间需要 53 分钟。

文章目录

此次项目 demo 实现了通过自定义注解添加到类中的属性上,直接导出即可,通过此注解可以实现简单的excel导出功能,值类型替换:例如:将0替换成女,将1替换成男,将on替换成开启,将off替换成关闭,还支持日期格式的导出,通过 dateFormat 选择执行的日期格式化标准,然后进行导出到 excel 中。

项目源码地址:

POI 导入依赖

org.apache.poi
poi
4.1.2
org.apache.poi
poi-ooxml
4.1.2
org.apache.poi
poi-ooxml-schemas
4.1.2

自定义 @ExcelProperty 注解

package com.bai.demo.annotation;import java.lang.annotation.*;/** * 

* 自定义注解导出Excel操作 * @Target 表示此注解可以被添加到实体类属性上 * @Retention 表示在程序运行时, 可以通过反射读取此注解中的信息 * @Documented 表示在生成JavaDoc时, 可以将此注解显示出来 *

* * @author bai * @date 2021/1/18 17:50 */@Target({
ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface ExcelProperty {
/** *

标记要导出的excel的列名称

* * @return 默认为 null */ String name() default ""; /** *

标记导出excel列的日期格式

* * @return 日期格式 */ String dateFormat() default ""; /** * 替换类型 *

* 使用说明: 如果你的实体类对象sex属性或者status属性实际获取到的值并不是0/1,或者ON/OFF那么就不会进行替换 *

* *

* 根据下划线分隔,翻译过来的意思就是将0替换成女,将1替换成男 * replace = {"0_女", "1_男"} * private int sex; * 根据下划线分隔,翻译过来的意思就是将ON替换成开启,将OFF替换成关闭 * replace = {"ON_开启", "OFF_关闭"} * private String status; *

* * @return 替换后的中文 */ String[] replace() default {
};}

自定义 ExcelConstants 常量常用类

package com.bai.demo.util;/** * 

* 自定义Excel中常用常量类 *

* * @author bai * @date 2021/1/18 17:50 */public interface ExcelConstants {
/** * 常用日期格式 */ public static final String YYYY_MM_DD = "yyyy-MM-dd"; public static final String YYYY_MM_DD_CHINESE = "yyyy年MM月dd日"; public static final String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm"; public static final String YYYY_MM_DD_HH_MM_CHINESE = "yyyy年MM月dd日 HH时mm分"; public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; public static final String YYYY_MM_DD_HH_MM_SS__CHINESE = "yyyy年MM月dd日 HH时mm分ss秒"; public static final String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.SSS"; public static final String YYYY_MM_DD_HH_MM_SS_SSS__CHINESE = "yyyy年MM月dd日 HH时mm分ss秒SSS毫秒"; public static final String YYYY_MM_DD_HH_MM_NO_SEPARATOR = "yyyyMMddHHmm"; public static final String YYYY_MM_DD_HH_MM_SS_NO_SEPARATOR = "yyyyMMddHHmmss"; /** * getter/setter方法 */ public static final String METHOD_GET = "get"; public static final String METHOD_SET = "set"; /** * excel导出的版本后缀 03 xls / 07 xlsx */ public static final String XLS = ".xls"; public static final String XLSX = ".xlsx"; /** * 通过网络请求 http 导出 excel 时 response 响应信息的一些设置规则 */ public static final String RESPONSE_CONTENT_TYPE = "application/octet-stream"; public static final String RESPONSE_CHARACTER_ENCODING_UTF8 = "utf-8"; public static final String RESPONSE_HEADER_NAME = "Content-disposition"; public static final String RESPONSE_HEADER_VALUE = "attachment; filename=";}

自定义Excel操作工具类

package com.bai.demo.util;import com.bai.demo.annotation.ExcelProperty;import org.apache.poi.ss.usermodel.Cell;import org.apache.poi.ss.usermodel.Row;import org.apache.poi.ss.usermodel.Sheet;import org.apache.poi.xssf.usermodel.XSSFWorkbook;import org.springframework.util.StringUtils;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.OutputStream;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.nio.charset.StandardCharsets;import java.text.SimpleDateFormat;import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;import java.util.*;/** * 

* 自定义Excel操作工具类 *

* * @author bai * @date 2021/1/18 17:50 */public class ExcelUtil {
/** *

* 自定义导出excel文件方法 *

* * @param request http请求 * @param response http响应 * @param data 需要导出的excel数据 list 集合 * @param clazz list集合中的泛型类模板 * @param excelName 导出的 excel 表名称 */ public static void exportFile(HttpServletRequest request, HttpServletResponse response, List
data, Class
clazz, String excelName) throws Exception {
// 创建工作簿 XSSFWorkbook workbook = new XSSFWorkbook(); // 调用导出excel的方法 ExcelUtil.export(workbook, data, clazz, excelName); // 生成文件名 String fileName = DateTimeFormatter.ofPattern(ExcelConstants.YYYY_MM_DD_HH_MM_SS_NO_SEPARATOR).format(LocalDateTime.now()) + excelName + ExcelConstants.XLSX; // 设置导出响应流规则 response.setContentType(ExcelConstants.RESPONSE_CONTENT_TYPE); response.setCharacterEncoding(ExcelConstants.RESPONSE_CHARACTER_ENCODING_UTF8); response.addHeader(ExcelConstants.RESPONSE_HEADER_NAME, ExcelConstants.RESPONSE_HEADER_VALUE + ExcelUtil.makeDownloadFileName(request, fileName)); // 将文件输出 try (OutputStream stream = response.getOutputStream()) {
workbook.write(stream); stream.flush(); } catch (Exception e) {
e.printStackTrace(); } } /** *

* 导出excel实例方法 *

* * @param list 导出的list集合 * @param clazz 导出的实体类模板class * @param sheetName excel工作表标题名称 */ private static void export(XSSFWorkbook workbook, List
list, Class
clazz, String sheetName) throws Exception {
Sheet sheet = workbook.createSheet(sheetName); // 创建工作表 sheet Row titleRow = sheet.createRow(0); // 创建标题行 titleRow Field[] fields = clazz.getDeclaredFields(); // 通过反射获取类模板中包含public,protected,private等属性 Map
methods = ExcelUtil.getMethod(clazz, ExcelConstants.METHOD_GET); // 获取类模板中所有属性的 getter 方法 // 创建标题行 for (int i = 0; i < fields.length; i++) {
ExcelProperty annotation = fields[i].getAnnotation(ExcelProperty.class); // 获取每一个属性上的 ExcelProperty 注解 ExcelUtil.setCellValues(titleRow, i, annotation.name()); // annotation.name() 就是实体类中属性上 ExcelProperty 注解中的 name 属性 } // 创建普通行 for (int i = 0; i < list.size(); i++) {
Row sheetRow = sheet.createRow(i + 1); Object targetObj = list.get(i); // 获取到集合中的一个对象 int currentIndex = 0; for (Field field : fields) {
Method method = methods.get(field.getName()); // 根据属性名称获取到属性 get 方法 Object value = method.invoke(targetObj); // 通过 Method.invoke(类对象) 来获取到 get 方法返回的 value 值 ExcelProperty annotation = field.getAnnotation(ExcelProperty.class); // 获取每一个属性上的 ExcelProperty 注解 // ExcelProperty 中 dateFormat 属性不为空表示此属性需要日期格式化 if (StringUtils.hasLength(annotation.dateFormat())) {
SimpleDateFormat sdf = ExcelUtil.switchDateFormatter(annotation.dateFormat()); ExcelUtil.setCellValues(sheetRow, currentIndex, sdf.format(value)); } // ExcelProperty 中 replace 属性不为空表示此属性需要格式转换 else if (annotation.replace().length > 0) {
for (String replaceText : annotation.replace()) {
String[] keys = replaceText.split("_"); // 注解自定义规则就是根据下划线进行分隔对象 if (keys.length > 2) throw new RuntimeException("replace关键字规格不正确"); if (keys[0].equals(value.toString())) {
ExcelUtil.setCellValues(sheetRow, currentIndex, keys[1]); } } } // 默认情况下的 ExcelProperty 只有 name,直接进行普通 excel 导出即可 else {
ExcelUtil.setCellValues(sheetRow, currentIndex, value); } currentIndex += 1; } } } /** *

* 判断属性需要格式化的日期规则 *

* * @param format 日期格式化公式 * @return 格式化对象 SimpleDateFormat */ private static SimpleDateFormat switchDateFormatter(String format) {
SimpleDateFormat sdf; switch (format) {
case ExcelConstants.YYYY_MM_DD: sdf = new SimpleDateFormat(ExcelConstants.YYYY_MM_DD); break; case ExcelConstants.YYYY_MM_DD_HH_MM_SS: sdf = new SimpleDateFormat(ExcelConstants.YYYY_MM_DD_HH_MM_SS); break; case ExcelConstants.YYYY_MM_DD_HH_MM_SS_SSS: sdf = new SimpleDateFormat(ExcelConstants.YYYY_MM_DD_HH_MM_SS_SSS); break; default: sdf = new SimpleDateFormat(ExcelConstants.YYYY_MM_DD_HH_MM_SS); break; } return sdf; } /** *

* 向指定位置的单元格中设置值 *

* * @param row 第几行 * @param currentIndex 第几个单元格 * @param value 添加进单元格的内容 */ private static void setCellValues(Row row, Integer currentIndex, Object value) {
Cell cell = row.createCell(currentIndex); // 通过反射判断传入的 value 类型 switch (value.getClass().getSimpleName()) {
case "String": cell.setCellValue(value.toString()); break; case "Integer": cell.setCellValue(Integer.parseInt(value.toString())); break; case "Float": cell.setCellValue(Float.parseFloat(value.toString())); break; case "Double": cell.setCellValue(Double.parseDouble(value.toString())); break; case "Long": cell.setCellValue(Long.parseLong(value.toString())); break; case "Boolean": cell.setCellValue(Boolean.parseBoolean(value.toString())); break; default: cell.setCellValue(""); break; } } /** *

* 根据 methodKeyWord 来获取指定类模板下的 getter/setter方法 * 用法例如: Map

map = this.getMethod(Student.class, "get"); * 那么返回值 map 中的值就是 Student 中所有属性的 get方法 *

* * @param clazz 类模板 * @param methodKeyWord get就是获取getter方法/set就是获取setter方法 */ private static Map
getMethod(Class
clazz, String methodKeyWord) {
Map
methodMap = new HashMap
(); /* 获取类模板所有属性 */ Field[] fields = clazz.getDeclaredFields(); for (Field value : fields) {
/* 获取属性名并组装方法名称 */ String fieldName = value.getName(); /* * methodKeyWord = 'get' * fieldName = 'name' * fieldName.substring(0, 1).toUpperCase() = 'N' * fieldName.substring(1) = 'ame()' * getMethodName = 'getName()' * */ String getMethodName = methodKeyWord + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); try {
Method method = clazz.getMethod(getMethodName); /* * 存储内容为: id,getId(); * name,getName(); * */ methodMap.put(fieldName, method); } catch (NoSuchMethodException e) {
e.printStackTrace(); System.err.println("无法获取字段的方法:" + value.getName()); } } return methodMap; } /** *

* 生成下载文件 *

* * @param request http请求 * @param fileName 文件名称 */ private static String makeDownloadFileName(HttpServletRequest request, String fileName) {
String agent = request.getHeader("User-Agent"); byte[] bytes = fileName.getBytes(StandardCharsets.UTF_8); if (agent.contains("MSIE") || agent.contains("Trident") || agent.contains("Edge")) {
return new String(bytes, StandardCharsets.UTF_8); } else {
return new String(bytes, StandardCharsets.ISO_8859_1); } }}

测试阶段

创建导出 Excel 相关测试的实体类

package com.bai.demo.model;import com.bai.demo.annotation.ExcelProperty;import com.bai.demo.util.ExcelConstants;import java.util.Date;/** * 导出excel相关的测试实体类 */public class Student implements java.io.Serializable {
@ExcelProperty(name = "序号") private Integer id; @ExcelProperty(name = "名称") private String name; @ExcelProperty(name = "年龄") private Integer age; @ExcelProperty(name = "性别", replace = {
"0_女", "1_男"}) private Integer sex; @ExcelProperty(name = "生日", dateFormat = ExcelConstants.YYYY_MM_DD_HH_MM_SS) private Date birth; @ExcelProperty(name = "家庭地址") private String address; public Student() {
super(); } public Student(Integer id, String name, Integer age, Integer sex, Date birth, String address) {
super(); this.id = id; this.name = name; this.age = age; this.sex = sex; this.birth = birth; this.address = address; } public Integer getId() {
return id; } public void setId(Integer id) {
this.id = id; } public String getName() {
return name; } public void setName(String name) {
this.name = name; } public Integer getAge() {
return age; } public void setAge(Integer age) {
this.age = age; } public int getSex() {
return sex; } public void setSex(Integer sex) {
this.sex = sex; } public Date getBirth() {
return birth; } public void setBirth(Date birth) {
this.birth = birth; } public String getAddress() {
return address; } public void setAddress(String address) {
this.address = address; } @Override public String toString() {
return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", sex=" + sex + ", birth=" + birth + ", address='" + address + '\'' + '}'; }}

添加 thymeleaf 依赖

org.springframework.boot
spring-boot-starter-thymeleaf

添加 StudentController 控制器编写导出 excel 接口

package com.bai.demo.controller;import com.bai.demo.model.Student;import com.bai.demo.util.ExcelUtil;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.Arrays;import java.util.Date;import java.util.List;@Controllerpublic class StudentController {
private static final List
students; static {
students = Arrays.asList( new Student(1, "张三", 25, 1, new Date(), "上海朱家嘴"), new Student(2, "李四", 33, 0, new Date(), "上海青浦"), new Student(3, "王五", 48, 1, new Date(), "上海陆家班"), new Student(4, "赵六", 19, 3, new Date(), "上海足球场") ); } @RequestMapping(value = "/exportStudentExcel") public void exportStudentExcel(HttpServletRequest request, HttpServletResponse response) {
try {
ExcelUtil.exportFile(request, response, students, Student.class, "学生信息表"); System.out.println("excel导出成功~"); } catch (Exception e) {
e.printStackTrace(); System.err.println("excel导出失败~"); } } @RequestMapping(value = {
"/", "/index"}) public ModelAndView goIndex() {
ModelAndView mv = new ModelAndView(); mv.setViewName("index"); return mv; } @GetMapping(value = "/hello") @ResponseBody public String hello() {
return "hello, world"; }}

在 resources/templates 资源目录下创建index.html 文件

    
主页导出学生记录

测试

启动项目 访问:localhost:8080/ 进入首页,然后点击 导出学生记录 请求后台接口导出 excel

效果截图

在这里插入图片描述

转载地址:http://gtqwi.baihongyu.com/

你可能感兴趣的文章
5620系列密码清除
查看>>
vncsever-centos&debian
查看>>
华为snmp模板
查看>>
kvm&xen挂载镜像文件
查看>>
SQL Server Union等操作时解决不同数据库字符集冲突的问题
查看>>
Linq GroupJoin(二)
查看>>
递归:访问页面的控件或文件夹的下文件
查看>>
DataGridView分頁控件
查看>>
Linq 使用entity framework查询视图返回重复记录的问题(转)
查看>>
项目中得到执行文件版本或其它信息
查看>>
WinForm DatagridView绑定大量数据卡顿的问题
查看>>
DataGridView或 DataTable导出到excel
查看>>
Ilist To DataTable
查看>>
SQL @@IDENTITY, IDENT_CURRENT,SCOPE_IDENTITY
查看>>
簡單工廠模式
查看>>
SQL Server的數據類型
查看>>
php的正则表达式&nbsp;&#039;/\b\w…
查看>>
ThinkPHP的标签制作及标签调用解析…
查看>>
thrift的lua实现
查看>>
编写高性能的Lua代码
查看>>