项目中经常遇到导出excel的功能,如果每次都写导出代码是很不方便的,现在基于EasyExcel上,使用aop封装一个导出excel的注解。
1、在项目添加需要的依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
2、新建一个导出数据实的实体类,并加上EasyExcel的注解,需要定义样式什么的,这个里直接使用EasyExcel的注解进行定义样式
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DemoData {
@ExcelProperty("主键")
private Long id;
@ExcelProperty("名字")
private String name;
@ExcelProperty("生日")
private Date birthday;
@ExcelProperty("年龄")
private Long age;
@ExcelProperty("地址")
private String address;
}
3、新建一个service,开发中这些数据是查询数据库的,现在只是简单准备数据
public interface ExcelDemoService {
List<ExcelDemoDTO> list();
}
@Service
public class ExcelDemoServiceImpl implements ExcelDemoService{
@Override
public List<DemoData> list() {
List<DemoData> list=new ArrayList<>();
list.add(new DemoData(1L,"jack",new Date(),12L,"广州"));
list.add(new DemoData(2L,"lonk",new Date(),15L,"北京"));
list.add(new DemoData(3L,"kolin",new Date(),17L,"上海"));
list.add(new DemoData(4L,"java",new Date(),11L,"南昌"));
list.add(new DemoData(5L,"spring",new Date(),11L,"广州"));
return list;
}
}
4、这时我们需要新建一个注解,用来标注需要导出excel的接口
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcelExport {
Class<?> value();//目标类型
String fileName();//文件名
String sheetName();//表名
}
5、有了注解,我们还需要定义一个切面来处理导出的逻辑
/**
* 定义切面
*/
@Component
@Aspect
public class ExcelExportAspect{
@Pointcut("@annotation(com.demo.annotation.ExcelExport)")
public void ExcelExportTarget(){}
//定义通知
@Around("ExcelExportTarget() && @annotation(excelParam)")
public void doProceed(ProceedingJoinPoint joinPoint, ExcelExport excelParam) throws Throwable {
//获取导出类型字节码对象
Class<?> clazz = excelParam.value();
//获取注解上设定的名字
String sheetName = excelParam.sheetName();
String fileName = excelParam.fileName();
//获取参数中的输出流对象
HttpServletResponse response=null;
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println(arg.getClass());
if (arg instanceof ResponseFacade || arg instanceof Response || arg instanceof HttpServletResponse) {
response = (HttpServletResponse)arg;
}
}
//执行目标方法 获取导出数据
List data= (List)joinPoint.proceed();
//设置响应参数
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
//执行导出
EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(data);
}
}
6、好了,这时我们已经有注解和对应的切面了,我们现在可以在需要导出excel的接口上使用这个注解实现导出excel了
*【注意】这时有两个注意点
- 1、方法返回值必段是一个List,且对应的数据是需要导出excel的数据
- 2、参数中必须定义HttpServletResponse
@RestController
@RequestMapping("/excel-demo")
public class ExcelDemoController {
@Autowired
private ExcelDemoService excelDemoService;
/**
*【注意】这时有两个注意点
* 1、方法返回值必段是一个List,且对应的数据是需要导出excel的数据
* 2、参数中必须定义HttpServletResponse
*/
@ExcelExport(value = DemoData.class,fileName = "测试aop导出Excel",sheetName = "重点人员数据")
@RequestMapping("/get-list-aop")
public List<DemoData> getListAop(HttpServletResponse response){
System.out.println(response.getClass());
List<DemoData> list = excelDemoService.list();
return list;
}
}
7、到这里你们已经完成了代码的需要你们写了,启动项目访问这个接口
localhost:8080/excel-demo/get-list-aop
打开excel
8、优化
文件名与表名我们现在是写在接口@ExcelExport这个注解上的,按照我们平时习惯,文件名与表名应该写在这个实现上面的,下面我们一起来优化一下
8.1新建一个注解,用于在实现类上标注文件名与表名
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcelInfo {
String fileName() default "";
String sheetName() default "";
}
8.2使用这个注解,修改刚刚那个实体类,实义文件名与表名
@Data
@AllArgsConstructor
@NoArgsConstructor
//添加注解
@ExcelInfo(fileName = "这是通过注解来定义的文件名",sheetName = "注解定义的表名")
public class DemoData {
@ExcelProperty("主键")
private Long id;
@ExcelProperty("名字")
private String name;
@ExcelProperty("生日")
private Date birthday;
@ExcelProperty("年龄")
private Long age;
@ExcelProperty("地址")
private String address;
}
8.3修改刚刚@ExcelExport那个注解,移除文件名与表名两个属性,修改后如下
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcelExport {
Class<?> value();
}
8.4由于指定义的文件名与表名位置发生了改变,那么我们要切面中需要修改一下获取文件名与表名的代码
/**
* 定义切面
*/
@Component
@Aspect
public class ExcelExportAspect{
@Pointcut("@annotation(com.demo.annotation.ExcelExport)")
public void ExcelExportTarget(){}
//定义通知
@Around("ExcelExportTarget() && @annotation(excelParam)")
public void doProceed(ProceedingJoinPoint joinPoint, ExcelExport excelParam) throws Throwable {
//获取导出类型字节码对象
Class<?> clazz = excelParam.value();
//-------------------------------修改部分--------------------------------------
//定义默认文件名与表名
String fileName="数据导出";
String sheetName="sheet1";
//获取注解上设定的名字
boolean isClassAnnotation = clazz.isAnnotationPresent(ExcelInfo.class);
if(isClassAnnotation){
ExcelInfo excelInfo = clazz.getAnnotation(ExcelInfo.class);
fileName= StringUtils.isEmpty(excelInfo.fileName())?fileName:excelInfo.fileName();
sheetName=StringUtils.isEmpty(excelInfo.sheetName())?sheetName:excelInfo.sheetName();;
}
//-----------------------------------------------------------------------------
//获取参数中的输出流对象
HttpServletResponse response=null;
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println(arg.getClass());
if (arg instanceof ResponseFacade || arg instanceof Response || arg instanceof HttpServletResponse) {
response = (HttpServletResponse)arg;
}
}
//执行目标方法 获取导出数据
List data= (List)joinPoint.proceed();
//设置响应参数
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
//执行导出
EasyExcel.write(response.getOutputStream(), ExcelDemoDTO.class).sheet(sheetName).doWrite(data);
}
}
8.5修改接口,移除文件名与表名
@ExcelExport(value = DemoData.class) //这里就变得好干净了,不会有业务代码了
@RequestMapping("/get-list-aop")
public List<DemoData> getListAop(HttpServletResponse response){
System.out.println(response.getClass());
List<DemoData> list = excelDemoService.list();
return list;
}
8.6重新访问
localhost:8080/excel-demo/get-list-aop
【注意】@ExcelExport(value = DemoData.class)这个注解是通用的,后面如果需要导出其它的数据,只需要新建一个实体类,在接口中使用这个注解即可,如:@ExcelExport(value = Student.class)