自定義註解
ExcelField
package com.hfepay.ai.platform.application.attend.annotation;
import java.lang.annotation.*;
/***
* @ClassName: ExcelField
* @Description: 導出註解
* @Author: wm_yu
* @Create_time: 9:46 2019-10-22
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelField {
/**
* 標題行
*/
String title();
/**
* 列排序
*/
int order() default 0;
/**
* 標紅條件,大於改值
*/
int colorInt() default -1;
/**
* 日期格式
*/
String dateFormat() default "yyyy-MM-dd HH:mm:ss";
}
使用到的反射工具類:
package com.hfepay.ai.platform.application.attend.util;
import com.alibaba.druid.sql.visitor.functions.Char;
import com.hfepay.ai.platform.application.attend.annotation.ExcelField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/***
* @ClassName: ClassReflectUtil
* @Description: 反射工具類
* @Author: wm_yu
* @Create_time: 9:51 2019-10-22
*/
public class ClassReflectUtil {
private final static Logger LOG = LoggerFactory.getLogger(ClassReflectUtil.class);
private static final String ORDER_ARR = "order_arr";
private static final String COLUMN_ARR = "column_arr";
private static final String TITLE_ARR = "title_arr";
/**
* 根據註解獲取到標題行,有排序按照排序,否則默認定義的字段順序
* @param clzz
* @return
*/
public static String[] getExcelTitle(Class<?> clzz){
Map<String, Object> map = sort(clzz);
return Optional.ofNullable(map).map(x -> (String[])x.get(TITLE_ARR)).orElse(new String[1]);
}
/**
* 獲取導出列(已排序)
* @param clzz
* @return
*/
public static String[] getColumns(Class<?> clzz){
Map<String, Object> map = sort(clzz);
return Optional.ofNullable(map).map(x -> (String[])x.get(COLUMN_ARR)).orElse(new String[1]);
}
/**
* 根據註解ExcelField對字段進行排序,生成排序數組,標題數組,列名數組
* @param clzz
* @return
*/
private static Map<String,Object> sort(Class clzz){
Assert.notNull(clzz,"class can not be null....");
Field[] fields = getAllFields(clzz);
long count = Arrays.stream(fields).filter(x -> x.isAnnotationPresent(ExcelField.class)).count();
//排序數組
int[] orderArr = new int[(int)count];
//嚴格和排序對應的標題數組
String[] titleArr = new String[(int)count];
//嚴格和排序對應的對象列名稱的數組
String[] columnArr = new String[(int)count];
int k = 0;
for (Field field : fields) {
if(field.isAnnotationPresent(ExcelField.class)){
if(field.isAccessible()){
field.setAccessible(true);
}
ExcelField annotation = field.getAnnotation(ExcelField.class);
orderArr[k] = annotation.order();
titleArr[k] = annotation.title();
columnArr[k] = field.getName();
k ++;
}
}
//排序
for (int i = 0; i < orderArr.length; i++) {
boolean flag = false;
for (int j = 0; j < orderArr.length - i - 1; j++) {
if(orderArr[j] > orderArr[j+1]){
int order = orderArr[j+1];
orderArr[j+1] = orderArr[j];
orderArr[j] = order;
String title = titleArr[j+1];
titleArr[j+1] = titleArr[j];
titleArr[j] = title;
String columnName = columnArr[j+1];
columnArr[j+1] = columnArr[j];
columnArr[j] = columnName;
flag = true;
}
}
if(!flag){
break;
}
}
Map<String, Object> map = new HashMap<>(3);
map.put(ORDER_ARR,orderArr);
map.put(COLUMN_ARR,columnArr);
map.put(TITLE_ARR,titleArr);
return map;
}
/**
* 獲取類所有字段(含超類字段)
* @param c
* @return
*/
public static Field[] getAllFields(Class c){
List<Field> fieldList = new ArrayList<>();
while (c!= null){
fieldList.addAll(new ArrayList<>(Arrays.asList(c.getDeclaredFields())));
c= c.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
fieldList.toArray(fields);
return fields;
}
/**
* 給null值給定默認值,時間類型排除不處理
*/
public static <T> void nullToDefaultValue(T obj){
Assert.notNull(obj,"source can not be null.....");
Field[] fields = ClassReflectUtil.getAllFields(obj.getClass());
Arrays.stream(fields)
.filter(x -> !Modifier.isFinal(x.getModifiers())
&& !Modifier.isStatic(x.getModifiers())
&&!Modifier.isAbstract(x.getModifiers()))
.forEach(x -> {
autoMatch(x,obj);
});
}
/**
* 類型匹配
* @param x 字段Field
* @param obj 對象
* @param <T>
*/
private static <T> void autoMatch(Field x,T obj){
if(!x.isAccessible()){
x.setAccessible(true);
}
try {
Object o = x.get(obj);
//null值處理
if(ObjectUtils.isEmpty(o)){
Class<?> type = x.getType();
if(type == Date.class){
return;
}
if(type == String.class){
x.set(obj,"");
}
else if(type == Integer.class){
x.set(obj,0);
}
else if(type == Double.class){
x.set(obj,0.0D);
}
else if(type == BigDecimal.class){
x.set(obj,new BigDecimal(0));
}
else if(type == Float.class){
x.set(obj,0.0f);
}
else if(type == Long.class){
x.set(obj,0L);
}
else if(type == HashMap.class){
x.set(obj,new HashMap<>(1));
}
else if(type == Boolean.class){
x.set(obj,false);
}
else if(type == Short.class){
x.set(obj,0);
}
else if(type == Char.class){
x.set(obj,0);
}
else if(type == Byte.class){
x.set(obj,0);
}
else if(type == ConcurrentMap.class){
x.set(obj,new ConcurrentHashMap<>(1));
}
else if(type.isArray()){
Object[] arr = {};
x.set(obj,arr);
} else if(type == List.class){
x.set(obj,Collections.EMPTY_LIST);
}else{
Object var = x.getType().newInstance();
x.set(obj,var);
}
}
} catch (IllegalAccessException | InstantiationException e) {
LOG.error("對象屬性:{} 不能通過反射獲取到值",x.toGenericString(),e.getMessage(),e);
}
}
}
excel導出工具類:excelUtil.java
package com.hfepay.ai.platform.application.attend.util.excel;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.hfepay.ai.platform.application.attend.annotation.ExcelField;
import com.hfepay.ai.platform.application.attend.util.ClassReflectUtil;
import com.hfepay.common.util.DateUtils;
import jxl.Workbook;
import jxl.write.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/***
* @ClassName: ExcelUtil
* @Description: 導出工具類
* @Author: wm_yu
* @Create_time: 15:25 2019-10-22
*/
public class ExcelUtil {
private final static Logger LOG = LoggerFactory.getLogger(ExcelUtil.class);
/**
* excel單元格寬度,默認爲20
*/
private static int width = 20;
/**
* 導出excel文件,瀏覽器直接下載
* @param response response流
* @param dataList 填充的數據集合,需要指定泛型
* @param sheetName sheet名稱
* @param fileName 下載的文件名稱
* @param bigTitle 大標題行,限定爲第一行
* @param <T>
*/
public static <T> void excelExport(HttpServletResponse response,List<T> dataList,String sheetName,String fileName,String bigTitle){
if(CollectionUtils.isEmpty(dataList)){
return;
}
Class<?> clzz = dataList.get(0).getClass();
//屬性集合
Field[] fields = ClassReflectUtil.getAllFields(clzz);
List<Field> fieldList = Arrays.stream(fields).filter(x -> x.isAnnotationPresent(ExcelField.class)).collect(Collectors.toList());
//維護列名稱和對應的索引,填充cell使用
Map<String, Integer> map = new ConcurrentHashMap<>();
/**
* 列名稱數組
*/
String[] columnArr = ClassReflectUtil.getColumns(clzz);
/**
* 標題行數組
*/
String[] titleArr = ClassReflectUtil.getExcelTitle(clzz);
//構建map
for (int i = 0; i < columnArr.length; i++) {
map.put(columnArr[i],i);
}
ServletOutputStream os = null;
try {
os = response.getOutputStream();
//創建workbook,sheet
WritableWorkbook workbook = Workbook.createWorkbook(response.getOutputStream());
//第一個sheet
WritableSheet sheet = workbook.createSheet(sheetName, 0);
//固定大小標題行
sheet.getSettings().setVerticalFreeze(1);
sheet.getSettings().setVerticalFreeze(2);
//初始化樣式
List<WritableCellFormat> styleList = initStyleFormat();
//構建大標題行,需要合併單元格 合併單元格,參數依次爲:列索引、行索引、列索引+需要合併的列的個數、行索引+需要合併的行的個數
sheet.mergeCells(0,0,columnArr.length - 1,0);
Label bigLabel = new Label(0, 0, bigTitle, styleList.get(0));
sheet.addCell(bigLabel);
for (int i = 0; i < titleArr.length; i++) {
Label excelTitle = new Label(i, 1, titleArr[i], styleList.get(0));
sheet.addCell(excelTitle);
// 設置列寬
sheet.setColumnView(i, width);
}
/*****************填充數據*********************/
//數據行從第三行開始,第一行爲大標題行,第二行爲小標題行
int rowIndex = 2;
for (T data : dataList) {
//循環field
for (Field field : fieldList) {
if(!field.isAccessible()){
field.setAccessible(true);
}
//判斷是否需要標紅數據
String dateFormat = field.getAnnotation(ExcelField.class).dateFormat();
int colorInt = field.getAnnotation(ExcelField.class).colorInt();
Object o = field.get(data);
Label label = null;
if(!ObjectUtils.isEmpty(o)){
if(o instanceof String){
//創建單元格,正常樣式
label = new Label(map.get(field.getName()), rowIndex,(String) o,styleList.get(1));
}
else if(o instanceof Integer){
//排除默認值
if((Integer)o > colorInt && colorInt != -1){
//數據標紅
label = new Label(map.get(field.getName()),rowIndex, String.valueOf(o),styleList.get(2));
}else{
label = new Label(map.get(field.getName()),rowIndex, String.valueOf(o),styleList.get(1));
}
}
else if(o instanceof Date){
label = new Label(map.get(field.getName()),rowIndex, DateUtils.format((Date) o,dateFormat),styleList.get(1));
}else {
label = new Label(map.get(field.getName()), rowIndex,String.valueOf(o),styleList.get(1));
}
//添加到行中
sheet.addCell(label);
}
}
++ rowIndex;
}
/*****************填充數據*********************/
//導出excel
// 處理中文的文件名
String fileNameCN = new String(fileName.getBytes("utf-8"),
"ISO-8859-1");
response.reset();
// 保存爲excel
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.addHeader("Content-Disposition", "attachment;filename="
+ fileNameCN);
workbook.write(); // 寫入到os流中
workbook.close();
os.flush();
os.close();
} catch (Exception e) {
LOG.error("導出excel異常:{}",e.getMessage(),e);
}
}
/**
* 初始化樣式
* @return
* @throws Exception
*/
private static List<WritableCellFormat> initStyleFormat() throws Exception{
// 列標題字體
WritableFont wfont = new WritableFont(WritableFont.ARIAL, 16,WritableFont.BOLD, false,jxl.format.UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.BLACK);
// 列標題字體 綠色
WritableFont wfontGreenTitle = new WritableFont(WritableFont.ARIAL, 16,WritableFont.BOLD, false,jxl.format.UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.GREEN);
// 正常字體
WritableFont wfont2 = new WritableFont(WritableFont.ARIAL, 10,WritableFont.NO_BOLD, false,jxl.format.UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.BLACK);
// 正常字體,標紅
WritableFont wfontRed = new WritableFont(WritableFont.ARIAL, 10,WritableFont.NO_BOLD, false,jxl.format.UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.RED);
// 正常字體,綠色
WritableFont wfontGreen = new WritableFont(WritableFont.ARIAL, 10,WritableFont.NO_BOLD, false,jxl.format.UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.GREEN);
WritableCellFormat titleFormat = new WritableCellFormat(wfont);
titleFormat.setVerticalAlignment(VerticalAlignment.CENTRE);
titleFormat.setAlignment(Alignment.CENTRE);
//給單元格加邊框
titleFormat.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN);
WritableCellFormat generalFormat = new WritableCellFormat(wfont2);
generalFormat.setVerticalAlignment(VerticalAlignment.CENTRE);
generalFormat.setAlignment(Alignment.CENTRE);
//給單元格加邊框
generalFormat.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN);
WritableCellFormat redFormat = new WritableCellFormat(wfontRed);
redFormat.setVerticalAlignment(VerticalAlignment.CENTRE);
redFormat.setAlignment(Alignment.CENTRE);
redFormat.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN);
WritableCellFormat greenFormat = new WritableCellFormat(wfontGreen);
greenFormat.setVerticalAlignment(VerticalAlignment.CENTRE);
greenFormat.setAlignment(Alignment.CENTRE);
greenFormat.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN);
WritableCellFormat greenTitleFormat = new WritableCellFormat(wfontGreenTitle);
greenTitleFormat.setVerticalAlignment(VerticalAlignment.CENTRE);
greenTitleFormat.setAlignment(Alignment.CENTRE);
greenFormat.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN);
List<WritableCellFormat> list = new ArrayList<WritableCellFormat>(){{
add(titleFormat);
add(generalFormat);
add(redFormat);
add(greenFormat);
add(greenTitleFormat);
}};
return list;
}
}