背景
写了不少的项目,不少的项目中都遇到过execl导出功能。前一阵子再次写导出功能,当时突然有个想法。能不用使用aop切面,将导出功能切出来写。以后用到的话,直接加注释多好。说干就干。
引入版本
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
核心方法
/**
* Description: AOP方法增强
*
* @author dzp
* @version 1.0
* @date: 2021-1-12 15:02
* @since JDK 1.8
*/
@Component
@Aspect
@Slf4j
public class ExportAspect {
@Pointcut("@annotation(com.example.demo.export.ExportExcel)")
public void exportPointcut() {
}
/***
* 导出不需要有返回值
* @param point
* @throws Throwable
*/
@Around("exportPointcut()")
public void doExport(ProceedingJoinPoint point) throws Throwable {
Object o = point.proceed();
if (o instanceof List) {
List list = (List) o;
MethodSignature signature = (MethodSignature) point.getSignature();
Method method;
method = point.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());
ExportExcel annotation = method.getAnnotation(ExportExcel.class);
//1.获取ExportExcel注解上beanClass的值
Class<?> aClass = annotation.beanClass();
Object[] args = point.getArgs();
//2.获取方法第一个参数值
HttpServletResponse response = (HttpServletResponse) args[0];
exportUtils(response, list, aClass);
}
}
/**
* 导出
* @param response
* @param list
* @param aClass
* @param <T>
*/
public <T> void exportUtils(HttpServletResponse response, List<T> list, Class<T> aClass) {
try {
XSSFWorkbook workbook = exportExcelByAnnotation(list, aClass);
OutputStream output = getOutputStream(response);
//FileOutputStream output = getOutputStreamForTest();
workbook.write(output);
output.close();
} catch (Exception e) {
e.printStackTrace();
response.setStatus(500);
}
}
/**
* 更改response中数据
* @param response
* @return
* @throws IOException
*/
private OutputStream getOutputStream(HttpServletResponse response) throws IOException {
response.reset(); // 清除buffer缓存
//处理请求
response.addHeader("Access-Control-Allow-Origin", "*");
response.setCharacterEncoding("utf-8");
response.setContentType("application/octet-stream;charset=utf-8");
response.addHeader("Content-Disposition", "attachment;filename=" + DateFormatUtils.format(new Date(), "yyyy-MM-dd") + ".xlsx");
return response.getOutputStream();
}
/**
* 导出
* @param list
* @param relClass
* @param <T>
* @return
* @throws Exception
*/
public <T> XSSFWorkbook exportExcelByAnnotation(List<T> list, Class<T> relClass) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");//日期类型数据导出格式化
XSSFWorkbook book = new XSSFWorkbook();
Sheet sheet = book.createSheet();
List<FieldEntity> fieldEntityList = Lists.newArrayList();
Field[] fields = relClass.getDeclaredFields();
Row row = sheet.createRow(0);
int titleNum = 0;
//利用反射获取对象属性
for (Field field : fields) {
FieldEntity fieldEntity = new FieldEntity();
//获取标注了注解的字段
ExportFiled fieldAnnotation = field.getAnnotation(ExportFiled.class);
//如果没有标注注解,直接跳过循环
if (fieldAnnotation == null) {
continue;
}
field.setAccessible(true);
fieldEntity.setFieldName(field.getName());
fieldEntity.setNumber(fieldAnnotation.number());
//获取字段标注的title
String titleName = fieldAnnotation.name();
Cell cell = row.createCell(titleNum);
cell.setCellType(CellType.STRING);
cell.setCellValue(titleName);
fieldEntityList.add(fieldEntity);
titleNum++;
field.setAccessible(false);
}
//按number排序
List<FieldEntity> fieldEntitiesAfterSorted = fieldEntityList.stream().sorted(Comparator.comparing(FieldEntity::getNumber)).collect(Collectors.toList());
List<String> fieldList = fieldEntitiesAfterSorted.stream().map(FieldEntity::getFieldName).collect(Collectors.toList());
for (int i = 1; i <= list.size(); i++) {
T obj = list.get(i - 1);
Row dataRow = sheet.createRow(i);
for (int j = 0; j < fieldList.size(); j++) {
String fieldName = fieldList.get(j);
Field field = obj.getClass().getDeclaredField(fieldName);
Cell dataCell = dataRow.createCell(j);
dataCell.setCellType(CellType.STRING);
field.setAccessible(true);
if (field.get(obj) != null) {
Object o = field.getType().toString();
if (("java.util.Date").equals(field.getType().getTypeName())) {
dataCell.setCellValue(sdf.format(field.get(obj)));
} else {
dataCell.setCellValue(String.valueOf(field.get(obj)));
}
} else {
dataCell.setCellValue("");
}
field.setAccessible(false);
}
}
return book;
}
@Data
private class FieldEntity {
private String number;
private String FieldName;
}
}
自定义注解
标记哪一个接口需要导出
/**
* Description: 导出Excel
*
* @author dzp
* @version 1.0
* @date: 2021-1-12 15:02
* @since JDK 1.8
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExportExcel {
/**
* 需要导出的实体class
*/
Class<?> beanClass();
}
例子如下:
@GetMapping("/get")
@ExportExcel(beanClass =User.class )
public List<User> select(HttpServletResponse response){
return userService.selectList();
}
标记需要导出那些字段和顺序
/**
* Description: 导出字段注解
*
* @author dzp
* @version 1.0
* @date: 2021-1-12 15:02
* @since JDK 1.8
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExportFiled {
/**
* 导出字段顺序
*/
String number();
/**
* 导出字段名称
*/
String name();
}
例子如下:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExportFiled(number = "0", name ="id" )
private Long id;
/**
* 姓名
*/
@ExportFiled(number = "1", name ="姓名" )
private String name;
/**
* 年龄
*/
@ExportFiled(number = "2", name ="年龄" )
private Integer age;
/**
* 邮箱
*/
@ExportFiled(number = "3", name ="邮箱" )
private String email;
/***
*性别
*/
@ExportFiled(number = "4", name ="性别" )
private UserSexEnum sex;
/**
* TableLogic注解表示逻辑删除 在执行删除的时候 会把删除方法调用成修改的方法
*/
@TableLogic
private Integer deleted;
@ExportFiled(number = "5", name ="时间" )
private Date time;
}
调用案例
controller层
@GetMapping("/get")
@ExportExcel(beanClass =User.class )
public List<User> select(HttpServletResponse response){
return userService.selectList();
}
成功截图
学习java群
java交流群:868794080