前阵子吧 用阿里的 easyexcel 做了导出。这阵子吧 ,人改需求了
前阵子需求导出的列是固定的。现在新需求是要做成可以选择导出的。比方说前台打钩了 id,title ,name,就导出这些数据。
所以 在使用阿里的 easyexcel 做导出的时候遇到了问题。阿里的是根据注解,有注解的列都导出了。暂时没发现有自定义这一说。
所以就使用poi自己做导出,觉得这个需求简单。直接让前台传递一个需要导出的字符串list 这样的。完事我拆分循环,判断、取值、导出。。。。所以就有了下边 的代码
public String getExcelData(MemberPoints points,String title){
switch (title){
case "ID":
return points.getId()+"";
case "会员ID":
return points.getMemberId() + "";
case "会员昵称":
return points.getMemberNickname();
case "会员手机号":
return points.getMemberPhone();
case "会员姓名":
return points.getMemberName();
case "类型":
if(points.getType()==1){
return "获得";
}else{
return "支出";
}
case "积分数量":
return points.getPoints()+"";
case "标题":
return points.getTitle();
case "详情":
return points.getContent();
default:
return "";
}
}
写一半 太累。 现在导出这个类 属性还是少的。那要是需求导出别的 那岂不是得写好多了。
所以就想 能不能根据用户传递给我的。需要导出的属性 直接get 到值了。
所以就有了下边的测试代码
MemberPoints points = new MemberPoints();
points.setId(1L);
points.setTitle("测试标题");
points.setContent("测试内容");
points.setAddtime(new Date());
//取得Class
Class classs = points.getClass();
String str = "Id,Title,Content,Addtime";
//反射 获得方法
Method method = classs.getMethod("getId" );
//执行方法
System.err.println(method.invoke(points));
大概就是 先写个模拟数据。 完事 反射 拿到 方法执行方法 获得值,测试类跑通了。说明方法可行,但是呢 还有 一般导出 表格 都是要有个表头的 。项目有用到 swagger 里的注解 所以就有了下边反射 拿注解 循环表头了
//获得属性
Field field = classs.getDeclaredField("title");
//获得属性的注解
ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
//获得注解的方法
Method method1 = apiModelProperty.getClass().getMethod("value");
System.err.println(method1.invoke(apiModelProperty));
到这里基本 测试类已经实现了,然后给封装成帮助类
这个里直接用了 swagger 的注解 实际可以自定义注解的
所以 有了下边的帮助类
import io.swagger.annotations.ApiModelProperty;
import org.apache.poi.hssf.usermodel.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.List;
/**
* @author YaoShiHang
* @title: ExcelUtil
* @projectName js-coupon
* @description: 表格导出 帮助类
* @date 2019/7/9 11:15
*/
public class ExcelUtil {
public static HSSFWorkbook export(List> list, String excelHeader, Class> clazz) throws Exception {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("sheet");
HSSFRow row = sheet.createRow((int) 0);
HSSFCellStyle style = wb.createCellStyle();
style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 水平居中
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 垂直居中
String[] headerArry = excelHeader.split(",");
//先反射设置表头
for (int i = 0; i < headerArry.length; i++) {
//反射拿到类属性
Field field = clazz.getDeclaredField(headerArry[i]);
//获得属性注解
ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
//获得注解方法
Method method1 = apiModelProperty.getClass().getMethod("value");
HSSFCell cell = row.createCell(i);
//执行注解方法 获得注解值
cell.setCellValue(method1.invoke(apiModelProperty)+"");
cell.setCellStyle(style);
sheet.autoSizeColumn(i);
}
//设置内容
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
Class clas = obj.getClass();
row = sheet.createRow(i+1);
for (int j = 0; j < headerArry.length; j++) {
Method method = clas.getMethod("get"+getMethodName(headerArry[j]) );
Type type = method.getGenericReturnType();// 获取返回值类型
HSSFCell cell = row.createCell(j);
if(type.getTypeName().equals("java.util.Date")){
//时间类型格式化 非时间类型 直接输出
cell.setCellValue(DateUtils.getCurrentDateTimeMilliSecond((Date) method.invoke(obj)));
}else{
cell.setCellValue(method.invoke(obj)+"");
}
cell.setCellStyle(style);
}
}
setSizeColumn(sheet);
return wb;
}
//首字母转大写
private static String getMethodName(String fildeName) throws Exception{
byte[] items = fildeName.getBytes();
items[0] = (byte) ((char) items[0] - 'a' + 'A');
return new String(items);
}
//设置poi 导出行自适应
private static void setSizeColumn(HSSFSheet sheet) {
for (int columnNum = 0; columnNum <= 8; columnNum++) {
int columnWidth = sheet.getColumnWidth(columnNum) / 256;
for (int rowNum = 0; rowNum < sheet.getLastRowNum(); rowNum++) {
HSSFRow currentRow;
//当前行未被使用过
if (sheet.getRow(rowNum) == null) {
currentRow = sheet.createRow(rowNum);
} else {
currentRow = sheet.getRow(rowNum);
}
if (currentRow.getCell(columnNum) != null) {
HSSFCell currentCell = currentRow.getCell(columnNum);
if (currentCell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
int length = currentCell.getStringCellValue().getBytes().length;
if (columnWidth < length) {
columnWidth = length;
}
}
}
}
sheet.setColumnWidth(columnNum, columnWidth * 256);
}
}
}
这样直接在controller里调用就好了
@RequestMapping(value = "points/download", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"})
@ApiOperation("用户积分 导出表格")
//@RequiresPermissions(value={"points:download"},logical= Logical.OR )
public void downloadPointsList(@RequestParam(defaultValue = "0",required = false) int type ,
@RequestParam(defaultValue = "",required = false) String begintime,
@RequestParam(defaultValue = "",required = false) String endtime,
@RequestParam(defaultValue = "",required = false) String headerStr, HttpServletResponse response) throws Exception {
List downLoads = memberPointsService.downloadBytime(type,begintime,endtime);
HSSFWorkbook wb = ExcelUtil.export(downLoads,headerStr,MemberPoints.class);
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment;filename=MemberPoints.xlsx");
OutputStream ouputStream = response.getOutputStream();
wb.write(ouputStream);
ouputStream.flush();
ouputStream.close();
}
可以肯定的是以上代码 性能相对比较低,也未经过测试。只是刚好拿来学习java反射。
关于反射的一些学习
优点:
(1)能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
(2)与Java动态编译相结合,可以实现无比强大的功能
缺点:
(1)使用反射的性能较低
(2)使用反射相对来说不安全
(3)破坏了类的封装性,可以通过反射获取这个类的私有方法和属性