JAVA反射方式实现简易通用EXCEL下载

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012557538/article/details/82382661

一:注解部分

package com.jianlejun.common.msoffice.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 用于绑定实体属性、Excel列名、Excel列索引之间的关系
 * @author allan
 */
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColField {
public String colName() ;//列名
public String position();//列标
}

二:实体部分

package com.jianlejun.common.msoffice.test;

import java.util.Date;

import com.jianlejun.common.msoffice.annotation.ColField;

public class User {
	@ColField(colName = "用户名", position = "0")
	private String userName;
	@ColField(colName = "年龄", position = "1")
	private int age;
	@ColField(colName = "地址", position = "3")
	private String address;
	@ColField(colName = "生日", position = "2")
	private Date birthDay;
	private String weight;

	public User(String userName, int age, Date birthday, String address) {
		this.userName = userName;
		this.age = age;
		this.birthDay = birthday;
		this.address = address;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Date getBirthDay() {
		return birthDay;
	}

	public void setBirthDay(Date birthDay) {
		this.birthDay = birthDay;
	}

	public String getWeight() {
		return weight;
	}

	public void setWeight(String weight) {
		this.weight = weight;
	}

}

三:抽象类

package com.jianlejun.common.msoffice.excel;

import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

public abstract class AbstractExcel {
	private Workbook workbook;
	private Sheet sheet;
	private static final String DEFAULT_SHEET_NAME = "DEFAULT";
	
	public abstract <T> Workbook createExcel(List<T> rowData) throws Exception;
	
	public abstract void buildTitle();// 标题非必须

	public abstract void buildHeader();// 表头非必须

	public abstract <T> void buildContent(List<T> rowData, Class<T> clazz) throws Exception;// 内容必须

	public abstract void buildTail();// 尾部非必须

	public Workbook buildWorkbook() {
		workbook = new HSSFWorkbook();
		return workbook;
	}

	public Sheet buildSheet() {
		int i = workbook.getNumberOfSheets();
		sheet = workbook.createSheet(DEFAULT_SHEET_NAME + "_" + (i + 1));
		return sheet;
	}

	public Sheet buildSheet(String sheetName) {
		int i = workbook.getNumberOfSheets();
		if (sheetName != null && !sheetName.isEmpty()) {
			sheet = workbook.createSheet(sheetName);
		} else {
			sheet = workbook.createSheet(DEFAULT_SHEET_NAME + "_" + (i + 1));
		}
		return sheet;
	}
	public Workbook initExcel() {
		this.buildWorkbook();
		this.buildSheet();
		return workbook;
	}
	public Workbook initExcel(String sheetName) {
		this.buildWorkbook();
		this.buildSheet(sheetName);
		return workbook;
	}
}

四:实现类

package com.jianlejun.common.msoffice.excel;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.logging.LogFactory;
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.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jianlejun.common.msoffice.annotation.ColField;

/**
 * @description 简易报表操作类
 * @author allan
 * @param <T>
 *
 */
public class EasyExcelOp extends AbstractExcel {
	private Logger log = LoggerFactory.getLogger(EasyExcelOp.class);
	private Workbook workbook;
	private Sheet sheet;
	private boolean isZeroRowUsed = false;// 采用组合思想,需特殊考虑下标为零的行
	private Map<String, Map<String, String>> colMap = new HashMap<String, Map<String, String>>();

	public EasyExcelOp() {
		workbook = initExcel();
	}

	public static EasyExcelOp getInstance() {
		return new EasyExcelOp();
	}

	@Override
	public <T> Workbook createExcel(List<T> rowData) throws Exception {
		sheet = workbook.getSheetAt(0);// 指明使用哪张sheet表
		Class clazz = rowData.get(0).getClass();
		colMap = this.getExcColRelation(clazz);
		this.buildTitle();
		this.buildHeader();
		this.buildContent(rowData, clazz);
		this.buildTail();
		return workbook;
	}

	/**
	 * 获取Excel列名、列key,列索引之间的映射关系
	 * 
	 * @param <T>
	 */
	private <T> Map<String, Map<String, String>> getExcColRelation(Class<T> clazz) throws Exception {
		Field[] fields = clazz.getDeclaredFields();
		for (Field f : fields) {
			if (!f.isAnnotationPresent(ColField.class)) {
				continue;
				//throw new Exception(f.getName() + ": is not annotated by annotation");
			}
			ColField ColFieldAnnoation = f.getDeclaredAnnotation(ColField.class);
			String position = ColFieldAnnoation.position();
			String colName = ColFieldAnnoation.colName();
			if (position == null || position.isEmpty()) {
				throw new Exception("position is required!");
			}
			if (colName == null) {
				throw new Exception("colName is required!");
			}
			if (!position.matches("[0-9]+")) {
				throw new Exception("can't convert position to Integer");
			}
			Map<String, String> m = new HashMap<String, String>();
			m.put(f.getName(), colName);
			colMap.put(position, m);
		}
		return colMap;
	}

	@Override
	public void buildHeader() {
		int begin = this.getRowTotal();
		Row row = sheet.createRow(begin);
		for (Entry<String, Map<String, String>> entry : colMap.entrySet()) {
			Cell cell = row.createCell(Integer.parseInt(entry.getKey()));
			for (String colName : entry.getValue().values()) {
				// 必定只有一个元素
				// TODO 是否可以不用循环方式来获取值,待优化
				cell.setCellValue(colName);
				break;
			}
		}
	}

	private int getRowTotal() {
		if (isZeroRowUsed) {
			return sheet.getLastRowNum() + 1;// 获取sheet最后一行的行号(索引从零开始)
		} else {
			isZeroRowUsed = true;
			return sheet.getLastRowNum();
		}
	}

	// 应用反射根据成员变量调用对应的setter/getter
	public <T> Object invoke(Object clazz, String fieldName)
			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException {
		PropertyDescriptor pd = new PropertyDescriptor(fieldName, clazz.getClass());
		// 从属性描述器中获取 get 方法
		Method method = pd.getReadMethod();
		Object obj = method.invoke(clazz);
		return obj == null ? "" : obj;
	}
	
	/**
	 * 构造EXCEL表格的主要内容体
	 */
	@Override
	public <T> void buildContent(List<T> rowData, Class<T> clazz) throws Exception {
		if (rowData == null || rowData.isEmpty()) {
			return;
		}
		int begin = this.getRowTotal();// 获取内容行从第几行开始填充
		for (int i = 0; i < rowData.size(); i++) {
			Row row = sheet.createRow(begin + i);
			for (int j = 0; j < colMap.size(); j++) {
				Cell cell = row.createCell(j);// TODO 单元格类型未设置,默认字符串
				for (String colNameVariable : colMap.get(String.valueOf(j)).keySet()) {
					cell.setCellValue(this.invoke(rowData.get(i), colNameVariable).toString());
				}
			}
		}
	}
	
	public Workbook getWorkbook() {
		return workbook;
	}

	public void setWorkbook(Workbook workbook) {
		this.workbook = workbook;
	}
	
	@Override
	public void buildTitle() {
		// TODO Auto-generated method stub
	}

	@Override
	public void buildTail() {
		// TODO Auto-generated method stub

	}
}

五:控制器输出文档

    @RequestMapping("/exportExc")
	public void exportExcel(@RequestBody Teacher teacher, HttpServletResponse resp) throws Exception {
		List<User> rowData = new ArrayList<User>//这里经过业务处理得到列表数据
		// 输出
		Workbook workbook = EasyExcelOp.getInstance().createExcel(rowData);
		outputExcel(workbook, resp);
	}
    public void outputExcel(Workbook workbook, HttpServletResponse resp) throws       IOException {
		String fileName = "xxxx记录_" + System.currentTimeMillis() + ".xls";
		OutputStream ops = resp.getOutputStream();
		resp.setContentType("application/octet-stream;charset=UTF-8");
		resp.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
		workbook.write(ops);
		ops.flush();
		ops.close();
	}

六:结束语

用传统的POI方式下载Excel文档,列变动,增加列,删除列,变动列位置等,都会引起程序的大修改,此方法只需要关注标识excel列的实体类即可,但此方式有一种缺陷,仅仅适用于简易的excel表格,复杂的合并神马的,请自行实现,本文方法并不适用

猜你喜欢

转载自blog.csdn.net/u012557538/article/details/82382661
今日推荐