设计模式 - 建造模式 - Excel导出实战
设计模式 - 建造模式 - Excel导出实战
概念
使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式
优点
1、建造者独立,易扩展。
2、便于控制细节风险。
缺点
1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
角色
1 Builder:为创建一个产品对象的各个部件指定抽象接口。
2 ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并 提供一个检索产品的接口。
3 Director:构造一个使用Builder接口的对象。
4 Product:表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口
2 ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并 提供一个检索产品的接口。
3 Director:构造一个使用Builder接口的对象。
4 Product:表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口
使用场景
1 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
2 当构造过程必须允许被构造的对象有不同表示时
2 当构造过程必须允许被构造的对象有不同表示时
UML类图
由于时间关系,这里仅仅提供不带属性和行为的UML类图
代码解析UML类图
Product
抽象产品 Workbook
具体产品:XSSFWorkbook
均采用Apache poi Excel 项目的类,需要加入依赖:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.11</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.11</version> </dependency>
由于Excel的生成需要经历:
1、创建工作簿 (导演类中可以改变,没有提供额外类结构)
2、创建表 (导演类中可以改变,没有提供额外类结构)
3、设置列宽 (导演类中可以改变,没有提供额外类结构)
4、创建样式实现 (类结构:AbstractSetStyle)
5、创建标题实现 (类结构:AbstractSetTitle)
6、将内容设置到单元格实现(类结构)
7、生成内容方式实现(类结构:AbstractSetContent)
8、获取属性值的获取方式(类结构:AbstractGetFieldValue)
为了通用性,我将这些模块化,下面给出代码
/** * 设置值到单元格抽象模块 * Created by laizhiyuan on 2017/6/5. */ public abstract class AbstractSetValueToCell { /** * 将值设置到单元格抽象方法 * @param fieldValue 属性值 * @return XSSFRichTextString 设置好的单元格 */ public abstract XSSFRichTextString setValue(Object fieldValue); }
/** * 默认设置值实现 * Created by laizhiyuan on 2017/6/5. */ public class DefaultSetValueToCell extends AbstractSetValueToCell { /** * 默认设置值得方式是直接装为字符串 * @param fieldValue * @return */ @Override public XSSFRichTextString setValue(Object fieldValue) { return new XSSFRichTextString(String.valueOf(fieldValue)); } }
/** * 获得属性值抽象模块 * Created by laizhiyuan on 2017/6/5. */ public abstract class AbstractGetFieldValue { /** * 抽象获得属性值方法 * @param obj 反射需要的对象 * @param fieldName 属性字符串 * @return */ public abstract Object getFiledValue(Object obj, String fieldName); }
/** * Created by laizhiyuan on 2017/6/5. */ public class DefaultGetValue extends AbstractGetFieldValue { @Override public Object getFiledValue(Object obj, String fieldName) { if (fieldName == null){ return ""; } return ReflectingHelper.getValueByField(obj, fieldName.toString()); } }
/** * 设置标题抽象模块 * Created by laizhiyuan on 2017/6/5. */ public abstract class AbstractSetTitle { /** * 设置标题抽象类 * @param sheet 表格 * @param columns 属性数组 * @param styleMap 样式库 * @param fieldTitleTable 属性标题对照表 */ public abstract void setTitle(Sheet sheet, String[] columns, Map<String, CellStyle> styleMap, Map<String, String> fieldTitleTable); }
/** * Created by laizhiyuan on 2017/6/5. */ public class DefaultSetTitle extends AbstractSetTitle { /** * 设置标题抽象类 * @param sheet 表格 * @param columns 属性数组 * @param styleMap 样式库 * @param fieldTitleTable 属性标题对照表 */ @Override public void setTitle(Sheet sheet, String[] columns, Map<String, CellStyle> styleMap, Map<String, String> fieldTitleTable){ //产生表格标题行 XSSFRow row = (XSSFRow) sheet.createRow(0); if(columns !=null && columns.length != 0){ for (int i = 0; i < columns.length; i++) { XSSFCell cell = row.createCell(i); cell.setCellType(XSSFCell.CELL_TYPE_STRING); cell.setCellStyle(styleMap.get(AbstractSetStyle.TITLE_STYLE_KEY)); XSSFRichTextString text = new XSSFRichTextString(fieldTitleTable.get(columns[i])); cell.setCellValue(text); } } } }
/** * 设置样式库抽象模块 * Created by laizhiyuan on 2017/6/5. */ public abstract class AbstractSetStyle { /** * 任何实现类,如果定义的是内容样式,存入map的key必须是这个,否则不生效 */ public static final String CONTENT_STYLE_KEY = "contentStyleKey"; /** * 任何实现类,如果定义的是标题样式且使用默认的设置Title方式, * 则存入map的key必须是这个,否则不生效,如果同时自定义了设置Title的实现,则不受这个限制 */ public static final String TITLE_STYLE_KEY = "titleStyleKey"; /** * 封装样式抽象方法 * * @param workbook 表格 * @param styleMap 样式库 */ public abstract void setStyle(Workbook workbook, Map<String, CellStyle> styleMap); }
/** * Created by laizhiyuan on 2017/6/5. */ public class DefaultSetStyle extends AbstractSetStyle { @Override public void setStyle(Workbook workbook, Map<String, CellStyle> styleMap) { // 生成一个样式 XSSFCellStyle style1 = (XSSFCellStyle) workbook.createCellStyle(); /** * 标题样式 */ // 前景色(蓝色) XSSFColor myColor = new XSSFColor(Color.LIGHT_GRAY); style1.setFillForegroundColor(myColor); // 设置单元格填充样式 style1.setFillPattern(XSSFCellStyle.SOLID_FOREGROUND); // 设置单元格边框 style1.setBorderBottom(XSSFCellStyle.BORDER_THIN); style1.setBorderLeft(XSSFCellStyle.BORDER_THIN); style1.setBorderRight(XSSFCellStyle.BORDER_THIN); style1.setBorderTop(XSSFCellStyle.BORDER_THIN); style1.setAlignment(XSSFCellStyle.ALIGN_CENTER); // 生成一个字体 XSSFFont font = (XSSFFont) workbook.createFont(); font.setFontHeightInPoints((short) 12); font.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD); // 把字体应用到当前的样式 style1.setFont(font); styleMap.put(TITLE_STYLE_KEY, style1); /** * 内容样式 */ XSSFCellStyle style2 = (XSSFCellStyle) workbook.createCellStyle(); style2.setBorderBottom(XSSFCellStyle.BORDER_THIN); style2.setBorderLeft(XSSFCellStyle.BORDER_THIN); style2.setBorderRight(XSSFCellStyle.BORDER_THIN); style2.setBorderTop(XSSFCellStyle.BORDER_THIN); style2.setAlignment(XSSFCellStyle.ALIGN_CENTER); style2.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER); // 生成另一个字体 XSSFFont font2 = (XSSFFont) workbook.createFont(); font2.setBoldweight(XSSFFont.BOLDWEIGHT_NORMAL); // 把字体应用到当前的样式 style2.setFont(font2); styleMap.put(CONTENT_STYLE_KEY, style2); } }
/** * 设置内容抽象模块 * Created by laizhiyuan on 2017/6/6. */ public abstract class AbstractSetContent<T> { public AbstractSetContent(Map<String, CellStyle> styleMap, Sheet sheet, AbstractGetFieldValue abstractGetFieldValue, AbstractSetValueToCell abstractSetValueToCell) { this.styleMap = styleMap; this.sheet = sheet; this.abstractGetFieldValue = abstractGetFieldValue; this.abstractSetValueToCell = abstractSetValueToCell; } /** * 样式库 */ protected Map<String, CellStyle> styleMap; /** * 组合表 */ protected Sheet sheet; /** * 组合获得属性 */ protected AbstractGetFieldValue abstractGetFieldValue; /** * 组合设置值到单元格 */ protected AbstractSetValueToCell abstractSetValueToCell; /** * 设置内容抽象方法 * @param dataSources * @param columns */ public abstract void setContent(Collection<T> dataSources, String[] columns); }
/** * 设置内容默认实现者 * Created by laizhiyuan on 2017/6/6. */ public class DefaultSetContent<T> extends AbstractSetContent<T>{ public DefaultSetContent(Map<String, CellStyle> styleMap, Sheet sheet, AbstractGetFieldValue abstractGetFieldValue, AbstractSetValueToCell abstractSetValueToCell) { super(styleMap, sheet, abstractGetFieldValue, abstractSetValueToCell); } @Override public void setContent(Collection<T> dataSources, String[] columns) { if (dataSources == null && dataSources.size() < 1 || columns == null && columns.length == 0){ return; } Iterator<T> iterator = dataSources.iterator(); XSSFRow row = null; XSSFCell cell = null; int rowIx = 0; Object fieldValue = null; XSSFRichTextString xssfValue = null; while (iterator.hasNext()){ rowIx++; /** * 创建行 一条数据一行 */ row = (XSSFRow) sheet.createRow(rowIx); T t = iterator.next(); for (int i = 0; i < columns.length; i++){ /** * 创建一个列, 一个标题一列 */ cell = row.createCell(i); cell.setCellType(XSSFCell.CELL_TYPE_STRING); cell.setCellStyle(styleMap.get(AbstractSetStyle.CONTENT_STYLE_KEY)); fieldValue = abstractGetFieldValue.getFiledValue(t, columns[i]); xssfValue = abstractSetValueToCell.setValue(fieldValue); cell.setCellValue(xssfValue); } } } }
/** * 获得属性值抽象模块 * Created by laizhiyuan on 2017/6/5. */ public abstract class AbstractGetFieldValue { /** * 抽象获得属性值方法 * @param obj 反射需要的对象 * @param fieldName 属性字符串 * @return */ public abstract Object getFiledValue(Object obj, String fieldName); }
/** * Created by laizhiyuan on 2017/6/5. */ public class DefaultGetValue extends AbstractGetFieldValue { @Override public Object getFiledValue(Object obj, String fieldName) { if (fieldName == null){ return ""; } return ReflectingHelper.getValueByField(obj, fieldName.toString()); } }
工具类:
/** * Created by laihziyuan on 2017/6/2. * * 反射工具类 */ public abstract class ReflectingHelper { private static final Logger logger = Logger.getLogger(ReflectingHelper.class); /** * 通过属性反射获得值 * @param t * @param fieldName * @return * @throws Exception */ public static Object getValueByField(Object t, String fieldName){ if (t == null || StringUtil.isEmpty(fieldName)){ return null; } Field field = getFieldByFieldName(t, fieldName); Object value = null; try { if (!field.isAccessible()){ /** * 设置属性为可访问 */ field.setAccessible(true); value = field.get(t); field.setAccessible(false); }else{ value = field.get(t); } } catch (IllegalAccessException e) { e.printStackTrace(); logger.error(e.toString()); } return value; } /** * 通过字符串获得对应的属性 * @param t * @param fieldName * @return */ public static Field getFieldByFieldName(Object t, String fieldName) { for (Class<?> superClass = t.getClass(); superClass != T.class; superClass = superClass .getSuperclass()) { try { return superClass.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { e.printStackTrace(); logger.error(e.toString()); } } return null; } }
Builder:抽象构建:AbstractExcelBuilder
/** * 建造者模式-抽象建造者 * 为创建一个 Workbook 对象的各个部件指定抽象接口 * * * Created by laizhiyuan on 2017/6/2. */ public abstract class AbstractExcelBuilder<T> { //记录日志 protected static Logger log = Logger.getLogger(AbstractExcelBuilder.class); /** * 构建对象 */ protected Workbook workbook; /** * 表格 */ protected Sheet sheet; /** * 样式库 */ protected Map<String, CellStyle> styleMap = new HashMap<String, CellStyle>(); /** * 属性装换为标题对照表 */ protected Map<String, String> fieldTitleTable = new HashMap<String, String>(); /** * 设置值到单元格模块 */ protected AbstractSetValueToCell abstractSetValueToCell; /** * 获得属性值模块 */ protected AbstractGetFieldValue abstractGetFieldValue; /** * 设置样式模块 */ protected AbstractSetStyle abstractSetStyle; /** * 设置标题模块 */ protected AbstractSetTitle abstractSetTitle; /** * 设置内容模块 */ protected AbstractSetContent<T> abstractSetContent; protected void addStyle(String key, CellStyle style){ styleMap.put(key, style); } protected CellStyle getStyle(String key){ return styleMap.get(key); } /** * 添加一个属性标题对照表 * * @param fieldName 属性名称 * @param titleName 标题名称 */ protected void addFieldTitleTable(String fieldName, String titleName){ fieldTitleTable.put(fieldName, titleName); } /** * 建造工作簿 */ public abstract AbstractExcelBuilder buildWorkbook(Workbook wb); /** * 建造表格 */ public abstract AbstractExcelBuilder buildSheet(String sheetName); public AbstractExcelBuilder buildColumn(int columnSize, boolean isAutoColumnWith){ if (isAutoColumnWith){ for (int i = 0; i < columnSize; i++){ sheet.autoSizeColumn(i); } return this; }else{ for (int i = 0; i < columnSize; i++){ sheet.setDefaultColumnWidth(i); } } return this; } /** * 建造内容 * @param dataSources 数据 * @param columns title */ public abstract AbstractExcelBuilder buildContent(Collection<T> dataSources, String[] columns, AbstractSetContent<T> abstractSetContent); /** * 建造标题 * @param titles * @return */ public abstract AbstractExcelBuilder buildTitle(String[] titles); /** * 建造样式 * @return */ public abstract AbstractExcelBuilder buildStyle(); /** * 生成最终构建好的对象 * @return */ public abstract void flush(HttpServletResponse response); /** * 获得属性值的方式,客户端可自定义 * @param getFieldValue */ public void getFieldValueImpl(AbstractGetFieldValue getFieldValue){ if (getFieldValue == null){ this.abstractGetFieldValue = new DefaultGetValue(); }else{ this.abstractGetFieldValue = getFieldValue; } } /** * 设置单元格值的方式,客户端自定义 * @param setValue */ public void setValueToCellImpl(AbstractSetValueToCell setValue){ if (setValue == null){ this.abstractSetValueToCell = new DefaultSetValueToCell(); }else{ this.abstractSetValueToCell = setValue; } } /** * 设置样式实现者,客户端自定义 * @param style */ public void setStyleImpl(AbstractSetStyle style){ if (style == null){ this.abstractSetStyle = new DefaultSetStyle(); }else{ this.abstractSetStyle = style; } } /** * 设置标题实现者,客户端自定义 * @param setTitle */ public void setTitleImpl(AbstractSetTitle setTitle){ if (setTitle == null){ this.abstractSetTitle = new DefaultSetTitle(); }else{ this.abstractSetTitle = setTitle; } } public void setContentImpl(AbstractSetContent<T> abstractSetContent){ if(abstractSetContent == null){ this.abstractSetContent = new DefaultSetContent<T>(styleMap, sheet, abstractGetFieldValue, abstractSetValueToCell); }else{ this.abstractSetContent = abstractSetContent; } } }
ConcreteBuilder:具体构建:ConcreteExcelBuilder
/** * 建造者模式-具体建造者 * * 实现Builder的接口以构造和装配该产品的各个部件 * * 这里是建造一个2007Excel * * Created by laizhiyuan on 2017/6/5. */ public class ConcreteExcelBuilder<T> extends AbstractExcelBuilder<T>{ /** * 建造工作簿 */ @Override public AbstractExcelBuilder buildWorkbook(Workbook wb) { if (workbook == null){ workbook = new XSSFWorkbook(); }else{ workbook = wb; } return this; } /** * 建造表格 */ @Override public AbstractExcelBuilder buildSheet(String sheetName) { sheet = workbook.createSheet(sheetName); return this; } /** * 建造内容 * @param dataSources 数据 * @param columns title */ @Override public AbstractExcelBuilder buildContent(Collection<T> dataSources, String[] columns, AbstractSetContent<T> abstractSetContent) { setContentImpl(abstractSetContent); this.abstractSetContent.setContent(dataSources, columns); return this; } /** * 建造标题 * @param titles * @return */ @Override public AbstractExcelBuilder buildTitle(String[] titles) { abstractSetTitle.setTitle(sheet, titles, styleMap, fieldTitleTable); return this; } /** * 建造样式 * @return */ @Override public AbstractExcelBuilder buildStyle() { abstractSetStyle.setStyle(workbook, styleMap); return this; } /** * 生成最终构建好的对象 * @return */ @Override public void flush(HttpServletResponse response) { ByteArrayOutputStream os = new ByteArrayOutputStream(); try { /** * 将工作簿对象转换为字节输出流对象 */ workbook.write(os); } catch (Exception e) { e.printStackTrace(); log.error(e.toString()); } BufferedInputStream bis = null; BufferedOutputStream bos = null; try { /** * 将工作簿字节流输出流转换为字节数组 */ byte[] content = os.toByteArray(); /** * 将字节数组转化为字节数组输入流对象 */ InputStream is = new ByteArrayInputStream(content); /** * 设置response参数,可以打开下载页面 */ response.reset(); response.setContentType("application/vnd.ms-excel;charset=utf-8"); response.setHeader( "Content-Disposition", "attachment;filename=" + new String((sheet.getSheetName() + DateFormatHelper.getTimeStr("yyyy-MM-dd", new Date()) + ".xlsx").getBytes(), "iso-8859-1")); ServletOutputStream out = response.getOutputStream(); /** * 封装为缓冲,减少读写次数,提高效率 */ bis = new BufferedInputStream(is); bos = new BufferedOutputStream(out); /** * 设定每次从内存读多少 */ byte[] buff = new byte[2048]; int bytesRead; /** * 循环从内存读取,并写到Resonse响应流中 */ while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) { bos.write(buff, 0, bytesRead); } /** * 异常部分 */ } catch (final IOException e) { e.printStackTrace(); log.error(e.toString()); } finally { /** * 关闭资源 */ if (bis != null) try { bis.close(); } catch (IOException e) { e.printStackTrace(); log.error(e.toString()); } if (bos != null) try { bos.close(); } catch (IOException e) { e.printStackTrace(); log.error(e.toString()); } } } }
Director:导演:ExcelDirector
/** * 建造者模式- 负责规范流程 * Created by laizhiyuan on 2017/6/2. */ public class ExcelDirector<T> { private AbstractExcelBuilder<T> abstractExcelBuilder; private String sheetName; private String[] columns; private Collection<T> dataSources; private Workbook workbook = null; private AbstractSetContent<T> abstractSetContent; private Map<Integer, Integer> columnWithMap; private boolean isAutoColumnWith = false; private Integer columnSize = null; public ExcelDirector(AbstractExcelBuilder<T> abstractExcelBuilder) { this.abstractExcelBuilder = abstractExcelBuilder; /** * 初始化时使用默认方式 */ overrideDefaultSetValueToCell(null) .overrideDefaultGetFieldValue(null) .overrideDefaultSetTitle(null) .overrideDefaultSetStyle(null) .overrideDefaultSetContent(null); } /** * 核心方法,这里决定构建一个复杂对象的流程 * @return */ public ExcelDirector construct(){ abstractExcelBuilder. /** * 先构建一个工作簿 */ buildWorkbook(workbook). /** * 再构建一个工作表 */ buildSheet(sheetName). /** * 再构建列宽 */ buildColumn(columnSize,isAutoColumnWith). /** * 再构建一个样式 */ buildStyle(). /** * 再构建表的标题 */ buildTitle(columns). /** * 最后构建内容 */ buildContent(dataSources, columns, abstractSetContent); /** * 至此构建一个Excel完毕 */ return this; } public ExcelDirector setColumnSize(int size){ this.columnSize = size; return this; } /** * 添加一个属性标题对照表 * * @param fieldName 属性名称 * @param titleName 标题名称 */ public ExcelDirector addFieldTitleTable(String fieldName, String titleName){ abstractExcelBuilder.addFieldTitleTable(fieldName, titleName); return this; } /** * 覆盖设置值到表格内容 * @param setValue * @return */ public ExcelDirector overrideDefaultSetValueToCell(AbstractSetValueToCell setValue){ abstractExcelBuilder.setValueToCellImpl(setValue); return this; } public Workbook getWorkbook() { return workbook; } public void setWorkbook(Workbook workbook) { this.workbook = workbook; } /** * 覆盖获得属性值 * @param getFieldValue * @return */ public ExcelDirector overrideDefaultGetFieldValue(AbstractGetFieldValue getFieldValue){ abstractExcelBuilder.getFieldValueImpl(getFieldValue); return this; } public ExcelDirector isAutoColumnWith(boolean isAutoColumnWith){ this.isAutoColumnWith = isAutoColumnWith; return this; } /** * 覆盖默认设置的样式 * @param setStyle * @return */ public ExcelDirector overrideDefaultSetStyle(AbstractSetStyle setStyle){ abstractExcelBuilder.setStyleImpl(setStyle); return this; } /** * 覆盖默认设置的标题 * @param setTitle * @return */ public ExcelDirector overrideDefaultSetTitle(AbstractSetTitle setTitle){ abstractExcelBuilder.setTitleImpl(setTitle); return this; } /** * 覆盖默认工作簿实现 * @param wb * @return */ public ExcelDirector overrideDefaultWorkbook(Workbook wb){ this.workbook = wb; return this; } public ExcelDirector overrideDefaultSetContent(AbstractSetContent<T> abstractSetContent){ this.abstractSetContent = abstractSetContent; return this; } /** * 写到响应流 * @param response */ public void flush(HttpServletResponse response){ abstractExcelBuilder.flush(response); } public String getSheetName() { return sheetName; } public ExcelDirector setSheetName(String sheetName) { this.sheetName = sheetName; return this; } public String[] getColumns() { return columns; } public ExcelDirector setColumns(String[] columns) { if (columnSize == null){ columnSize = columns.length; } this.columns = columns; return this; } public Collection<T> getDataSources() { return dataSources; } public ExcelDirector setDataSources(Collection<T> dataSources) { this.dataSources = dataSources; return this; } public ExcelDirector setColumnWith(Map<Integer, Integer> columnWithMap){ this.columnWithMap = columnWithMap; return this; } }
测试
/** * Created by laizhiyuan on 2017/6/7. */ @Controller public class Main { static String[] titles = new String[]{"id", "name", "address"}; static List<TestExcelExport> testExcelExports = new ArrayList<TestExcelExport>(); static{ TestExcelExport testExcelExport = new TestExcelExport(); testExcelExport.setName("test1"); testExcelExport.setAddress("address1"); testExcelExport.setId("1"); testExcelExports.add(testExcelExport); TestExcelExport testExcelExport2 = new TestExcelExport(); testExcelExport2.setName("test2"); testExcelExport2.setAddress("address2"); testExcelExport2.setId("2"); testExcelExports.add(testExcelExport2); TestExcelExport testExcelExport3 = new TestExcelExport(); testExcelExport3.setName("test3"); testExcelExport3.setAddress("address3"); testExcelExport3.setId("3"); testExcelExports.add(testExcelExport3); } static class TestExcelExport{ private String id; private String name; private String address; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } @RequestMapping( value = "/export", method = RequestMethod.GET ) public void export(HttpServletResponse response){ AbstractExcelBuilder<TestExcelExport> builder = new ConcreteExcelBuilder<TestExcelExport>(); ExcelDirector<TestExcelExport> excelExportExcelDirector = new ExcelDirector<TestExcelExport>(builder); excelExportExcelDirector .addFieldTitleTable("id", "唯一标识符") .addFieldTitleTable("name", "名字") .addFieldTitleTable("address", "地址") .setColumns(titles) .setDataSources(testExcelExports) /** * 如果某个模块默认的实现不能满足或不符合你的需求,这里你可以自己实现并覆盖默认的 */ //.overrideDefaultSetContent() //.overrideDefaultGetFieldValue() .setSheetName("测试Excel导出") .construct().flush(response); } }