重构:以Java POI 导出EXCEL为例2

简介: 前言上一篇博文已经将一些对象抽象成成员变量以及将一些代码块提炼成函数。这一节将会继续重构原有的代码,将一些函数抽象成类,增加成员变量,将传入的参数合成类等等。上一篇博文地址:http://www.cnblogs.

前言

上一篇博文已经将一些对象抽象成成员变量以及将一些代码块提炼成函数。这一节将会继续重构原有的代码,将一些函数抽象成类,增加成员变量,将传入的参数合成类等等。

上一篇博文地址:http://www.cnblogs.com/fixzd/p/8982739.html

正文

我们先来看看上一篇博文重构后的代码,还是有点长

public class ExportExcel<T> {
    private HSSFWorkbook workbook;
    
    public ExportExcel() {
        this(new HSSFWorkbook());
    }

    public ExportExcel(HSSFWorkbook workbook) {
        this.workbook = workbook;
    }

    public void exportExcel(Collection<T> dataset, OutputStream out) {
        exportExcel("测试POI导出EXCEL文档", null, dataset, out, "yyyy-MM-dd");
    }

    public void exportExcel(String[] headers, Collection<T> dataset, OutputStream out) {
        exportExcel("测试POI导出EXCEL文档", headers, dataset, out, "yyyy-MM-dd");
    }

    public void exportExcel(String[] headers, Collection<T> dataset, OutputStream out, String pattern) {
        exportExcel("测试POI导出EXCEL文档", headers, dataset, out, pattern);
    }

    public void exportExcel(String title, String[] headers, Collection<T> dataset, OutputStream out, String pattern) {
        // 生成一个表格
        HSSFSheet sheet = workbook.createSheet(title);
        
        // 生成数据标题和数据行样式
        HSSFCellStyle rowTirtleStyle = getRowTitleStyle();
        HSSFCellStyle rowDataStyle = getRowDataStyle();
        
        //创建数据标题和数据行
        createRowTitle(headers, sheet, rowTirtleStyle);
        createRowData(dataset, pattern, sheet, rowDataStyle);
        
        //写入流
        writeExecl(out);
    }

    /**
     * Description:写入到OutputStream
     */
    private void writeExecl(OutputStream out) {
        try {
            workbook.write(out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Description: 产生数据行
     */
    private void createRowData(Collection<T> dataset, String pattern, HSSFSheet sheet, HSSFCellStyle rowDataStyle) {
        // 遍历集合数据,产生数据行
        Iterator<T> it = dataset.iterator();
        int index = 0;
        while (it.hasNext()) {
            index++;
            HSSFRow row = sheet.createRow(index);
            T t = (T) it.next();
            // 利用反射,根据javabean属性的先后顺序,动态调用getXxx()方法得到属性值
            Field[] fields = t.getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                HSSFCell cell = row.createCell(i);
                cell.setCellStyle(rowDataStyle);
                Field field = fields[i];
                String fieldName = field.getName();
                String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                try {
                    Class tCls = t.getClass();
                    Method getMethod = tCls.getMethod(getMethodName, new Class[] {});
                    Object value = getMethod.invoke(t, new Object[] {});
                    // 判断值的类型后进行强制类型转换
                    String textValue = null;
                    if (value instanceof Boolean) {
                        boolean bValue = (Boolean) value;
                        textValue = "男";
                        if (!bValue) {
                            textValue = "女";
                        }
                    } else if (value instanceof Date) {
                        Date date = (Date) value;
                        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
                        textValue = sdf.format(date);
                    } else {
                        // 其它数据类型都当作字符串简单处理
                        textValue = value.toString();
                    }
                    // 如果不是图片数据,就利用正则表达式判断textValue是否全部由数字组成
                    if (textValue != null) {
                        Pattern p = Pattern.compile("^//d+(//.//d+)?$");
                        Matcher matcher = p.matcher(textValue);
                        if (matcher.matches()) {
                            // 是数字当作double处理
                            cell.setCellValue(Double.parseDouble(textValue));
                        } else {
                            HSSFRichTextString richString = new HSSFRichTextString(textValue);
                            HSSFFont font3 = workbook.createFont();
                            font3.setColor(HSSFColor.BLUE.index);
                            richString.applyFont(font3);
                            cell.setCellValue(richString);
                        }
                    }
                } catch (SecurityException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } finally {
                    // 清理资源
                }
            }
        }
    }

    /**
     * Description: 产生表格标题行
     */
    private void createRowTitle(String[] headers, HSSFSheet sheet, HSSFCellStyle rowTirtleStyle) {
        HSSFRow row = sheet.createRow(0);
        for (int i = 0; i < headers.length; i++) {
            HSSFCell cell = row.createCell(i);
            cell.setCellStyle(rowTirtleStyle);
            HSSFRichTextString text = new HSSFRichTextString(headers[i]);
            cell.setCellValue(text);
        }
    }

    /**
     * Description:生成数据标题样式
     */
    private HSSFCellStyle getRowTitleStyle() {
        HSSFCellStyle style = workbook.createCellStyle();
        // 设置这些样式
        style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style.setBorderTop(HSSFCellStyle.BORDER_THIN);
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        
        // 生成一个字体
        HSSFFont font = workbook.createFont();
        font.setColor(HSSFColor.VIOLET.index);
        font.setFontHeightInPoints((short) 12);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        
        // 把字体应用到当前的样式
        style.setFont(font);
        
        return style;
    }
    
    /**
     * Description:生成数据行样式
     */
    private HSSFCellStyle getRowDataStyle() {
        HSSFCellStyle style = workbook.createCellStyle();
        // 设置这些样式
        style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style.setBorderTop(HSSFCellStyle.BORDER_THIN);
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        
        // 生成另一个字体
        HSSFFont font = workbook.createFont();
        font.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
        
        style.setFont(font);
                
        return style;
    }

}
View Code

那么接下来我们要如何重构呢?我们从问题的角度来重构吧

  1. 在上面代码中我们可以看到有两个创建样式的函数,这两个函数getRowTitleStyle()和getRowDataStyle()可以说是导出execl的默认样式。在这里我们思考下,如果我们对导出的样式有变动是不是又要修改ExportExcel类?
        /**
         * Description:生成数据标题样式
         */
        private HSSFCellStyle getRowTitleStyle() {
            HSSFCellStyle style = workbook.createCellStyle();
            // 设置这些样式
            style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
            style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
            style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
            style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
            style.setBorderRight(HSSFCellStyle.BORDER_THIN);
            style.setBorderTop(HSSFCellStyle.BORDER_THIN);
            style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
            
            // 生成一个字体
            HSSFFont font = workbook.createFont();
            font.setColor(HSSFColor.VIOLET.index);
            font.setFontHeightInPoints((short) 12);
            font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
            
            // 把字体应用到当前的样式
            style.setFont(font);
            
            return style;
        }
        
        /**
         * Description:生成数据行样式
         */
        private HSSFCellStyle getRowDataStyle() {
            HSSFCellStyle style = workbook.createCellStyle();
            // 设置这些样式
            style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
            style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
            style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
            style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
            style.setBorderRight(HSSFCellStyle.BORDER_THIN);
            style.setBorderTop(HSSFCellStyle.BORDER_THIN);
            style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
            style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
            
            // 生成另一个字体
            HSSFFont font = workbook.createFont();
            font.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
            
            style.setFont(font);
                    
            return style;
        }
    View Code

     

  2. 接下来我们看看导出的主方法exportExcel(String title, String[] headers, Collection<T> dataset, OutputStream out, String pattern)。有没有觉得参数有点多了,能不能将一些参数合并成一个类或者去除没用的参数?
  3. 我们再来看看产生数据行函数createRowData的数据处理方式,如下。虽然这里只有两个if...else,但是我们能不能用什么设计模式将他们提取出来,方便以后增加类型时不必修改原有的类?
                        if (value instanceof Boolean) {
                            boolean bValue = (Boolean) value;
                            textValue = "男";
                            if (!bValue) {
                                textValue = "女";
                            }
                        } else if (value instanceof Date) {
                            Date date = (Date) value;
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                            textValue = sdf.format(date);
                        } else {
                            // 其它数据类型都当作字符串简单处理
                            textValue = value.toString();
                        }

     

  4. 我们在思考下第三个问题,数据的转换不仅仅只有上面那么按部就班的,就好比一辆车的状态有正常,损坏、维修中、报废等等,但是在数据库中是以0、1、2、3来存储的,那么有什么好的方法可以在不修改原有代码下进行转换呢?

好了,这篇博文就围绕着上面四个问题进行重构,大家可以试着按照这几个问题对上面代码进行重构,相信经过自己思考和动手后会对代码的设计有进一步的理解。

 

问题1:如果我们对导出的样式有变动是不是又要修改ExportExcel类?

针对上面问题,我们是不是可以将样式创建抽象成类?

    /**
     * Description:生成数据标题样式
     */
    private HSSFCellStyle getRowTitleStyle() {
        HSSFCellStyle style = workbook.createCellStyle();
        // 设置这些样式
        style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style.setBorderTop(HSSFCellStyle.BORDER_THIN);
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        
        // 生成一个字体
        HSSFFont font = workbook.createFont();
        font.setColor(HSSFColor.VIOLET.index);
        font.setFontHeightInPoints((short) 12);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        
        // 把字体应用到当前的样式
        style.setFont(font);
        
        return style;
    }

从上面代码可以置顶HSSFCellStyle的创建需要用到Workbook对象,所以接口就不适用。

然后上面代码中可以看出一个整体的样式还包含了字体,那么就可以设计一个抽象类,如下

public abstract class AbstractCellStyle {
    HSSFWorkbook workbook;
    HSSFCellStyle style;
    HSSFFont font;

    public AbstractCellStyle(HSSFWorkbook workbook) {
        this.workbook = workbook;
        style = workbook.createCellStyle();
        font = workbook.createFont();
    }
    
    public abstract void setStyle();
    
    public abstract void setFont();

    
    public HSSFCellStyle getCellStyle() {
        style.setFont(font);
        return style;
    }
}

该抽象类AbstractCellStyle有一个有参构造函数、三个成员变量、一个获得样式的方法和两个抽象方法setStyle和setFont。

子类通过继承AbstractCellStyle,然后重写方法setStyle和setFont即可。

构造函数需要传入Workbook,然后在构造函数里创建样式和字体对象,最后调用getCellStyle方法获得HSSFCellStyle即可。

我这里弄了两个默认的样式了,如下

默认数据行样式类

DefaultDataCellStyle.java

public class DefaultDataCellStyle extends AbstractCellStyle{
    public DefaultDataCellStyle(HSSFWorkbook workbook) {
        super(workbook);
    }

    @Override
    public void setStyle() {
        style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style.setBorderTop(HSSFCellStyle.BORDER_THIN);
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
    }
    @Override
    public void setFont() {
        font.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
    }

}
View Code

默认一个数据标题样式,

DefaultTitleCellStyle.java

public class DefaultTitleCellStyle extends AbstractCellStyle{
    public DefaultTitleCellStyle(HSSFWorkbook workbook) {
        super(workbook);
    }

    @Override
    public void setStyle() {
        style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style.setBorderTop(HSSFCellStyle.BORDER_THIN);
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
    }
    @Override
    public void setFont() {
        font.setColor(HSSFColor.VIOLET.index);
        font.setFontHeightInPoints((short) 12);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
    }
}
View Code

然后ExportExcel这个类也需要做出相应的修改,修改内容如下

  1. 删除两个函数getRowTitleStyle()和getRowDataStyle()
  2. 增加两个成员变量,且将Workbook也抽象成成员变量
        private HSSFWorkbook workbook;
        private AbstractCellStyle titleCellStyle;//标题行样式
        private AbstractCellStyle dataCellStyle;//数据行样式
  3. 修改构造函数,如下
    public ExportExcel() {
            this(new HSSFWorkbook());
        }
    
        /**
         * 这里可以定义两个常量,但是这里需要workbook,所以就没有抽取出来
         * @param workbook
         */
        public ExportExcel(HSSFWorkbook workbook) {
            this(workbook,new DefaultTitleCellStyle(workbook),new DefaultDataCellStyle(workbook));
        }
    
        public ExportExcel(HSSFWorkbook workbook, AbstractCellStyle titleCellStyle, AbstractCellStyle dataCellStyle) {
            this.workbook = workbook;
            this.titleCellStyle = titleCellStyle;
            this.dataCellStyle = dataCellStyle;
        }
  4. 其他调用getRowTitleStyle()和getRowDataStyle()方法的修改成调用成员变量的方法。

重构后

那么现在我们再来看看这个问题,如果我们对导出的样式有变动是不是又要修改ExportExcel类?

现在我们需要修改导出的标题样式或者数据行样式的话,我们只需要写个类继承AbstractCellStyle,然后设置字体和样式即可,是不是很方便。如果你有更好的建议在评论处留言哦


 

问题2:导出的主方法exportExcel参数有点多了,能不能将一些参数合并成一个类或者去除没用的参数?

 原来的如下

public void exportExcel(String title, String[] headers, Collection<T> dataset, OutputStream out, String pattern) {...}

现在让我们来思考思考下execl文件,一个execl文件可能有多个工作簿(也就是sheet),如下

每个工作簿(sheet)都有自己的表格,每个表格都有自己的行和列

在我们到处这里是以每一行对应数据库中一张表的一条记录,每一列就好比数据库中的一张表的一个属性。

现在我们现将每一列抽象成一个类,每一列中包含一个标题和数据所对应的实体属性,例如,学号是标题,学号在实体Student中对应的属性是id

抽象出来的类如下

public class CellEntity {
    private String title;
    private String filedName;
    
    //set get方法...
}

接下来我们把每个工作簿(sheet)也抽象成一个类,这个类中包含多个列(CellEntity)、工作簿名称(sheetName)、数据集合。

public class SheetEntity {
    private String sheetName;

    private List<CellEntity> cellEntitys;

    private Collection dataset;

    //set get方法  
}

最后修改导出的主方法exportExcel和其他一些参数的获取

public void exportExcel(SheetEntity sheetEntity, OutputStream out) {...}

 

问题3:函数createRowData的数据处理能不能用什么设计模式将他们提取出来,方便以后增加类型时不必修改原有的类?

    String textValue = null;
    if (value instanceof Boolean) {
        boolean bValue = (Boolean) value;
        textValue = "男";
        if (!bValue) {
            textValue = "女";
        }
    } else if (value instanceof Date) {
        Date date = (Date) value;
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        textValue = sdf.format(date);
    } else {
        // 其它数据类型都当作字符串简单处理
        textValue = value.toString();
    }

从上面代码中可以看出value被判断是哪种类型,然后再进行赋值操作,显示被判断是否是Boolean,然后判断Date,最后都被当成字符串处理。

这里博主采用的是如下方法,将数据转换抽象成一个抽象类,如下

public abstract class AbstractDataHandler {
    private AbstractDataHandler abstractDataHandler;

    public AbstractDataHandler(AbstractDataHandler abstractDataHandler) {
        this.abstractDataHandler = abstractDataHandler;
    }

    public abstract String dataHandle(Object value);

    protected String nextHandle(Object value) {
        if (abstractDataHandler != null) {
            return abstractDataHandler.dataHandle(value);
        }
        return null;
    }
}

子类通过集成该抽象类,实现dataHandle方法,如果是当前类型,则处理后返回,否则调用抽象类AbstractDataHandler的nextHandle方法继续调用下个数据处理方法。现在来看看我写的几个数据处理类

Boolean数据处理

public class BooleanDataHandler extends AbstractDataHandler {

    public BooleanDataHandler(AbstractDataHandler abstractDataHandler) {
        super(abstractDataHandler);
    }

    @Override
    public String dataHandle(Object value) {
        if (value instanceof Boolean) {
            boolean bValue = (Boolean) value;
            String textValue = "是";
            if (!bValue) {
                textValue = "否";
            }
            return textValue;
        } else {
            return nextHandle(value);
        }
    }

}

Date数据处理

 

public class DateDataHandler extends AbstractDataHandler {
    
    public DateDataHandler(AbstractDataHandler abstractDataHandler) {
        super(abstractDataHandler);
    }

    @Override
    public String dataHandle(Object value) {
        if (value instanceof Date) {
            Date date = (Date) value;
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return sdf.format(date);
        } else {
            return nextHandle(value);
        }
    }

}

 

String数据处理

 

 

public class StringDataHandler extends AbstractDataHandler {

    public StringDataHandler(AbstractDataHandler abstractDataHandler) {
        super(abstractDataHandler);
    }

    @Override
    public String dataHandle(Object value) {
        return value.toString();
    }

}

 

 

 

然后创建一个工厂类DataHandlerFactory,工具类调用这个工厂类的dataHandle方法,数据处理就会按照Boolean-->Date-->String流程走下去,都没有处理就返回null

public class DataHandlerFactory {

    private static AbstractDataHandler dataHandler = new BooleanDataHandler(
            new DateDataHandler(
                    new StringDataHandler(null)));

    public static String dataHandle(Object value) {
        return dataHandler.dataHandle(value);
    }
}

 修改后的代码如下,是不是简便了许多

       

 -------------->     

 

现在如果需要增加默认的类型处理,只需要增加AbstractDataHandler的子类,然后修改DataHandlerFactory工厂类即可。

问题4:数据的转换并不是按照预期进行的那么有什么好的方法可以在不修改原有代码下进行转换呢?

关于这个问题,我们需要增加一个转换接口,然后将数据转换交给子类来实现即可。其实在实体类CellEntity增加一个类型转换的成员变量即可。接口如下

public interface DataConversion {
    String transferData(Object data);
}

实体类CellEntity也需要做相应的修改,增加一个成员变量即可

public class CellEntity {
    private String title;
    private String fieldName;
    private DataConversion conversion;
    ......
}

然后到处工具类中也要做修改,由原先交给工厂处理的修改为判断cellEntity对象的成员变量conversion是否为null,为null则交给默认的数据处理类处理,不为null则交给conversion处理

    if (cellEntity.getConversion() == null)
        textValue = DataHandlerFactory.dataHandle(value);
    else
        textValue = cellEntity.getConversion().transferData(value);

就按上面车辆状态来举个相应的例子吧,判断传入的类型是否为0或者1,0返回正常,1返回异常,其他的都返回null,如下

public class CarStatusExportConversion implements DataExportConversion {

    @Override
    public String transferData(Object data) {
        if (data == null) return null;
        Integer carStatus = (Integer) data;
        switch (carStatus){
            case 0:
                return "正常";
            case 1:
                return "异常";
        }
        return null;
    }
}

完整的工具类和测试代码

ExportExecl工具类

public class ExportExcel<T> {
    private HSSFWorkbook workbook;
    private AbstractCellStyle titleCellStyle;//标题行样式
    private AbstractCellStyle dataCellStyle;//数据行样式

    public ExportExcel() {
        this(new HSSFWorkbook());
    }

    /**
     * 这里可以定义两个常量,但是这里需要workbook,所以就没有抽取出来
     * @param workbook
     */
    public ExportExcel(HSSFWorkbook workbook) {
        this(workbook,new DefaultTitleCellStyle(workbook),new DefaultDataCellStyle(workbook));
    }

    public ExportExcel(HSSFWorkbook workbook, AbstractCellStyle titleCellStyle, AbstractCellStyle dataCellStyle) {
        this.workbook = workbook;
        this.titleCellStyle = titleCellStyle;
        this.dataCellStyle = dataCellStyle;
    }

    public void exportExcel(SheetEntity sheetEntity, OutputStream out) {
        // 生成一个表格
        HSSFSheet sheet = workbook.createSheet(sheetEntity.getSheetName());
        // 生成数据标题和数据行样式
        HSSFCellStyle rowTirtleStyle = titleCellStyle.getCellStyle();
        HSSFCellStyle rowDataStyle = dataCellStyle.getCellStyle();
        
        //创建数据标题和数据行
        createRowTitle(sheetEntity.getCellEntitys(), sheet, rowTirtleStyle);
        createRowData(sheetEntity.getCellEntitys(),sheetEntity.getDataset(), sheet, rowDataStyle);
        
        //写入流
        writeExecl(out);
    }

    /**
     * Description:写入到OutputStream
     */
    private void writeExecl(OutputStream out) {
        try {
            workbook.write(out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Description: 产生数据行
     */
    private void createRowData(List<CellEntity> cellEntitys, Collection<T> dataset, HSSFSheet sheet, HSSFCellStyle rowDataStyle) {
        // 遍历集合数据,产生数据行
        Iterator<T> it = dataset.iterator();
        int index = 0;
        while (it.hasNext()) {
            index++;
            HSSFRow row = sheet.createRow(index);
            T t = (T) it.next();
            for (int i = 0; i < cellEntitys.size(); i++) {
                HSSFCell cell = row.createCell(i);
                cell.setCellStyle(rowDataStyle);
                try {
                    String textValue = null;

                    CellEntity cellEntity = cellEntitys.get(i);
                    Object value = PropertyUtils.getProperty(t, cellEntity.getFiledName());

                    if (cellEntity.getConversion() == null)
                        textValue = DataHandlerFactory.dataHandle(value);
                    else
                        textValue = cellEntity.getConversion().transferData(value);

                    cell.setCellValue(textValue);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Description: 产生表格标题行
     */
    private void createRowTitle(List<CellEntity> cellEntitys, HSSFSheet sheet, HSSFCellStyle rowTirtleStyle) {
        HSSFRow row = sheet.createRow(0);
        for (int i = 0; i < cellEntitys.size(); i++) {
            HSSFCell cell = row.createCell(i);
            cell.setCellStyle(rowTirtleStyle);
            HSSFRichTextString text = new HSSFRichTextString(cellEntitys.get(i).getTitle());
            cell.setCellValue(text);
        }
    }

}
View Code

Test导出测试类

public class Test {

    public static void main(String[] args) {
        // 测试学生
        ExportExcel<Student> ex = new ExportExcel<Student>();
        // 测试图书
        ExportExcel<Book> ex2 = new ExportExcel<Book>();

        List<Student> studentList = getStudentList();

        try {


            OutputStream out = new FileOutputStream("E://a.xls");

            ex.exportExcel(getStudentSheetEntity("学生",studentList), out);

            out.close();
            System.out.println("excel导出成功!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static SheetEntity getStudentSheetEntity(String sheetName,List<Student> studentList){

        List<CellEntity> cellEntitys = new ArrayList<CellEntity>();
        cellEntitys.add(new CellEntity("学号","id"));
        cellEntitys.add(new CellEntity("姓名","name"));
        cellEntitys.add(new CellEntity("年龄","age"));
        cellEntitys.add(new CellEntity("性别","sex", new SexDataConversion()));
        cellEntitys.add(new CellEntity("出生日期","birthday"));

        SheetEntity entity = new SheetEntity(sheetName,cellEntitys , studentList);

        return entity;
    }

    private static List<Student> getStudentList() {
        List<Student> datas = new ArrayList<Student>();
        datas.add(new Student(10000001, "张三", 20, true, new Date()));
        datas.add(new Student(20000002, "李四", 24, false, new Date()));
        datas.add(new Student(30000003, "王五", 22, true, new Date()));
        return datas;
    }

}

class SexDataConversion implements DataConversion{

    @Override
    public String transferData(Object data) {
        if (data instanceof Boolean) {
            boolean bValue = (Boolean) data;
            String textValue = "男";
            if (!bValue) {
                textValue = "女";
            }
            return textValue;
        }
        return null;
    }
}
View Code

 

这一篇修改后的github地址如下

github地址:https://github.com/rainbowda/utils/tree/master/poi/src/main/java/com/utils/poi/update_2

后续会增加其他各种各样的工具项目,觉得不错的给个star,谢谢


作者: 云枭zd
Github: Github地址
出处: https://www.cnblogs.com/fixzd/
版权声明:本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。
目录
相关文章
|
1月前
|
前端开发
实现Excel文件和其他文件导出为压缩包,并导入
实现Excel文件和其他文件导出为压缩包,并导入
30 1
|
1月前
|
数据格式 UED
记录一次NPOI库导出Excel遇到的小问题解决方案
【11月更文挑战第16天】本文记录了使用 NPOI 库导出 Excel 过程中遇到的三个主要问题及其解决方案:单元格数据格式错误、日期格式不正确以及合并单元格边框缺失。通过自定义单元格样式、设置数据格式和手动添加边框,有效解决了这些问题,提升了导出文件的质量和用户体验。
181 3
|
1月前
|
Java API Apache
|
1月前
|
存储 Java API
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
71 4
|
2月前
|
JavaScript 前端开发 数据处理
Vue导出el-table表格为Excel文件的两种方式
Vue导出el-table表格为Excel文件的两种方式
114 6
|
Java C# C++
重构:以Java POI 导出EXCEL为例
重构 开头先抛出几个问题吧,这几个问题也是《重构:改善既有代码的设计》这本书第2章的问题。 什么是重构? 为什么要重构? 什么时候要重构? 接下来就从这几个问题出发,通过这几个问题来系统的了解重构的意义。
1404 0
|
2天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
4天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
4天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
5天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
21 3