前几天被派到一个小项目中做临时维护,工作地点不方便且不说,项目代码那叫一个恶心...

代码几乎完全没有注释。这应该是我们天朝大部分程序员的习惯,代码不写注释,给后面维护的同事带来多大麻烦啊!
几百行的JS代码放在JSP文件中,而且没有格式。个人觉得这么长的代码提取到JS文件中比较好,都堆在JSP中使程序可读性极差!
HTML代码没有结构可言。基本的缩进都没有,读这种代码那叫一个欲哭无泪啊!HTML混合JSTL以及Struts2的标签,叫人头大!
超长的方法体。接触项目的第三天见到一个600多行的方法,感觉很头大,更极品的是后来发现了一个1100行的方法,当时的感觉是见到高人了...
文件组织极其混乱。自己写的JS与引入的开源JS框架混在一起,JS开源框架文件也是乱放。
命名也是非常混乱,完全做不到见名知义。
代码乱放。本来不是该模块的代码非要组织到该模块中...
其它问题。如同样代码多处编写等
其中有一个问题就是对Excel文件的导入解析(就是那个600多行的方法),那么长的方法,我都懒得去读一遍;代码长还不是主要问题,主要问题是这种代码肯定是需要一次就得写一次。有鉴于此,我便花些时间试着利用Java的泛型与反射机制写了一个通用的工具类,但绞尽脑汁也没有写地太完善,思来想去总是不尽人意;佑于水平不高,也只能如此,测试了一下勉强能用,只是容错能力不强,发表于此,以待后用...

列名使用粗体、水平居中、垂直居中、12号字,没有其它样式,也没有提供自定义单元格样式的功能,如果有特殊需求,此类不适用。

package com.ninemax.common.util;
 
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
 
import jxl.BooleanCell;
import jxl.Cell;
import jxl.DateCell;
import jxl.NumberCell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.biff.EmptyCell;
import jxl.format.Alignment;
import jxl.format.VerticalAlignment;
import jxl.write.Blank;
import jxl.write.DateTime;
import jxl.write.Label;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
 
/**
 * 操作Excel工具类(使用开源项目JXL操作Excel).<br />
 * 需要严格按照方法定义来使用, 容错能力不高.<br />
 * 实体类应该严格按照JavaBean规范定义, 数据类型仅支持基本类型、java.util.Date、java.lang.String
 * 
 * @author zhangyh (2013-01-11)
 */
public class ExcelUtil {
    
    /**
     * getExcelStream(T[], String[], String[], String)的中转方法, 可直接接受List集合数据
     * @see {@link ExcelUtil#getExcelStream(Object[], String[], String[], String)}
     */
    public static <T> OutputStream getExcelStream(List<T> dataList, String [] beanProperties,
            String [] columnNames, String sheetName) throws Exception {
        if (null != dataList && dataList.size() > 0) {
            Class<?> newType = dataList.get(0).getClass();
            @SuppressWarnings("unchecked")
            T [] tempArray = (T []) Array.newInstance(newType, dataList.size());
            dataList.toArray(tempArray);
            
            return getExcelStream(tempArray, beanProperties, columnNames, sheetName);
        }
        return null;
    }
    
    /**
     * getExcelStreamWithPointOutProperties(T[], String[], String[], String)的中转方法, 
     * 可直接接受List集合数据, 方法第二个参数接受指明转换到Excel的JavaBean属性, 顺序任意.
     * @see {@link ExcelUtil#getExcelStreamWithPointOutProperties(Object[], String[], String[], String)}
     */
    public static <T> OutputStream getExcelStreamWithPointOutProperties(List<T> dataList, 
            String [] beanProperties, String [] columnNames, String sheetName) 
            throws Exception {
        if (null != dataList && dataList.size() > 0) {
            Class<?> newType = dataList.get(0).getClass();
            @SuppressWarnings("unchecked")
            T [] tempArray = (T []) Array.newInstance(newType, dataList.size());
            dataList.toArray(tempArray);
            
            return getExcelStreamWithPointOutProperties(tempArray, beanProperties, 
                columnNames, sheetName);
        }
        return null;
    }
    
    /**
     * <p>将Java对象组成的集合转换成Excel数据, 以输出流形式返回, 可以实现下载、存储在磁盘等.</p>
     * <p>Excel文件列按JavaBean属性定义顺序生成, 还需要注意Excel列名数与排除掉的属性数之和必须与
     *     JavaBean属性总数相等.</p>
     * @param dataList 需要转换到Excel的Java对象数组
     * @param columnNames Excel列名(即JavaBean属性对应的列名, 如:name >> '姓名'), 必须按JavaBean
     *     属性定义顺序给出, 还需要注意跳过excludeProperties指定的排除掉的属性
     * @param excludeProperties 排除掉的JavaBean属性名
     * @param sheetName Excel文件表名
     * @return 生成的Excel文件输出流(ByteArrayOutputStream)
     * @throws Exception 
     */
    public static <T> OutputStream getExcelStream(T [] dataList, String [] columnNames, 
            String [] excludeProperties, String sheetName) throws Exception {
        if (null == columnNames) {
            throw new Exception("Excel列名必须指定.");
        }
        // 取得数组成员的类型Class对象
        Class<?> clazz = dataList.getClass().getComponentType();
        Field [] fields = clazz.getDeclaredFields();
        
        List<String> excludePropertyList = Collections.emptyList();
        if (excludeProperties != null && excludeProperties.length > 0) {
            excludePropertyList = Arrays.asList(excludeProperties);
        }
        
        if (fields.length != columnNames.length + excludePropertyList.size()) {
            throw new Exception("给定Excel列名为" + columnNames.length + "个, 排除掉的" +
                    "属性为" + excludePropertyList.size() + "个, 实体类" + 
                    clazz.getSimpleName() +    "共有" + fields.length + 
                    "个属性,个数不匹配.");
        }
        // 列名与Bean有效属性一一对应
        String [] beanProperties = new String [columnNames.length];
        int i = 0;
        for(Field field : fields) {
            String fieldName = field.getName();
            if (excludePropertyList == null || !excludePropertyList.contains(fieldName)) {
                // 当给定排除属性数和列名数之和与Bean属性数不等时会有异常
                beanProperties[i++] = fieldName;
            }
        }
        Method [] getterMethods = parseGetterMethods(beanProperties, clazz);
        return getExcelStream(dataList, getterMethods, columnNames, sheetName);
    }
    
    /**
     * <p>将Java对象组成的集合转换成Excel数据, 以输出流形式返回, 可以实现下载、存储在磁盘等.</p>
     * <p>可按任意顺序取任意个数的Bean属性导出到Excel, Excel列顺序将与给定beanProperties顺序保持一致.</p>
     * @param data 需要生成Excel文件的Java数据, beanProperties即是T中的属性
     * @param beanProperties 需要转换到Excel中的Bean属性
     * @param colNames Excel使用的列名, 需要与beanProperties一一对应
     * @param sheetName Excel表名
     * @return 生成的Excel输出流(ByteArrayOutputStream)
     * @throws Exception
     */
    public static <T> OutputStream getExcelStreamWithPointOutProperties (T [] data, 
            String [] beanProperties, String [] colNames, String sheetName) throws Exception {
        if (beanProperties == null || colNames == null) {
            throw new Exception("必须给出有效的JavaBean属性及相应的Excel列名.");
        }
        if (beanProperties.length > colNames.length) {
            throw new Exception("给出的Excel列名为" + colNames.length + 
            "个, 给出的有效Bean属性为" + beanProperties.length + "个, 个数不匹配.");
        }
        Class<?> clazz = data.getClass().getComponentType();
        Method [] getterMethods = parseGetterMethods(beanProperties, clazz);
        
        return getExcelStream(data, getterMethods, colNames, sheetName);
    }
    
    // 解析JavaBean属性对应的getter方法
    private static Method [] parseGetterMethods(String [] properties, Class<?> clazz) 
            throws Exception {
        Method [] getterMethods = new Method [properties.length];
        int i = 0;
        for(String property : properties) {
            String getterName = "get" + property.substring(0, 1).toUpperCase() + property.substring(1);
            try {
                getterMethods[i++] = clazz.getMethod(getterName, (Class []) null);
            } catch (SecurityException e) {
                throw new Exception("属性" + property + "对应的getter方法可能无法访问.", e);
            } catch (NoSuchMethodException se) {
                throw new Exception("属性" + property + "没有对应的getter方法.", se);
            }
        }
        return getterMethods;
    }
    
    /**
     * 具体执行方法, 公开的方法getExcelStreamWithPointOutProperties和getExcelStream都是为调用此方法作相应准备. 
     * 方法中调用getters并将值填进相应的单元格中, 最后将Excel文件以输出流的形式返回.
     * @param data Java集合数据, 公开方法中的List形式也会转为数组形式.
     * @param getterMethods 需要转换成Excel列的JavaBean属性对应的getters方法
     * @param colNames Excel列名, 与JavaBean属性对应
     * @param sheetName Excel表名
     * @return Excel文件对应的输出流
     * @throws Exception
     */
    private static <T> OutputStream getExcelStream(T [] data, Method [] getterMethods, 
            String [] colNames,    String sheetName) throws Exception {
        if (null == sheetName) {
            sheetName = "Sheet_1";
        }
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        WritableWorkbook workbook = Workbook.createWorkbook(os);
        WritableSheet sheet = workbook.createSheet(sheetName, 0);
        
        WritableCellFormat cellFormat = new WritableCellFormat();
        cellFormat.setAlignment(Alignment.CENTRE);
        cellFormat.setVerticalAlignment(VerticalAlignment.CENTRE);
        cellFormat.setFont(new WritableFont(WritableFont.ARIAL, 12, WritableFont.BOLD));
        
        int col = 0;
        for(Method method : getterMethods) {
            int row = 0;
            // 第一行(列名)采用粗体、15号字, 并居中对齐
            sheet.addCell(new Label(col, row++, colNames[col], cellFormat));    
            for(T t : data) {
                // 调用getter方法, 并将返回值添加到Excel的单元格中
                invokeGetterMethod(method, t, sheet, row++, col);    
            }
            col++;
        }
        workbook.write();
        workbook.close();
        return os;
    }
    
    /**
     * 调用JavaBean中的取值方法, 并将返回值按照JavaBean属性的数据类型填充到Excel指定的单元格.
     * @param getterMethod JavaBean中的取值方法Method
     * @param o 调用Getter方法的JavaBean实例
     * @param sheet Excel表
     * @param rowIndex Excel表中的行索引
     * @param colIndex Excel表中的列索引
     * @throws Exception
     */
    private static void invokeGetterMethod(Method getterMethod, Object o, WritableSheet sheet,
            int rowIndex, int colIndex) throws Exception {
        Class<?> type = getterMethod.getReturnType();
        Object returnVal = getterMethod.invoke(o, (Object []) null);
        if (null == returnVal) {    // getter方法返回值为null
            sheet.addCell(new Blank(colIndex, rowIndex));
        }else if (String.class.isAssignableFrom(type) || char.class.isAssignableFrom(type)
                || Character.class.isAssignableFrom(type)) {
            // getter方法返回值为String或char
            sheet.addCell(new Label(colIndex, rowIndex, returnVal.toString()));
        } else if (Number.class.isAssignableFrom(type) || double.class.isAssignableFrom(type)
                || int.class.isAssignableFrom(type) || long.class.isAssignableFrom(type)
                || short.class.isAssignableFrom(type) || float.class.isAssignableFrom(type)) {
            // getter方法返回值为数字类型
            sheet.addCell(new jxl.write.Number(colIndex, rowIndex, ((Number)returnVal).doubleValue()));
        } else if (Date.class.isAssignableFrom(type)) {        // getter方法返回值为java.util.Date类型
            sheet.addCell(new DateTime(colIndex, rowIndex, (Date) returnVal));
        } else if (Boolean.class.isAssignableFrom(type) || boolean.class.isAssignableFrom(type)) {
            // getter方法返回值为布尔类型
            sheet.addCell(new jxl.write.Boolean(colIndex, rowIndex, (Boolean) returnVal));
        } else {    // 不支持其它的返回值类型
            throw new Exception("getter方法: " + getterMethod.getName() + 
                "的返回值类型不被支持.");
        }
    }
    
    /**
     * <p>解析Excel, 将Excel扁平数据解析封装为Java对象, 以Java集合形式返回.</p>
     * <p>由于封装并不完善, 需要严格按照方法指定调用方式使用才可. 解析顺序必须按照Excel文件中列顺序</p>
     * @param excel 需要解析的Excel文件
     * @param beanProperties JavaBean的属性名, 以数组方式给出; 必须依照Excel文件所定义列顺序给出.
     * @param columnTypes JavaBean属性的数据类型, 必需与columns数组数据一一对应
     * @param clazz JavaBean的Class对象, 必须指定, 否则无法使用反射
     * @return Excel解析所得JavaBean组成的List
     * @throws Exception
     */
    public static <T> List<T> parseExcel(File excel, String [] beanProperties, 
            Class<?> [] propertyTypes, Class<T> clazz) throws Exception {
        return parseExcel(new FileInputStream(excel), beanProperties, propertyTypes, clazz);
    }
    
    /**
     * 解析Excel, 将Excel扁平数据解析封装为Java对象.
     * @param inputStream Excel文件对应的输入流.
     * @param columns JavaBean的属性名
     * @param columnTypes JavaBean属性的数据类型
     * @param clazz JavaBean类的Class对象
     * @return Excel解析所得JavaBean组成的List
     * @throws Exception
     * @see {@link ExcelUtil#parseExcel(File, String[], Class[], Class)}
     */
    public static <T> List<T> parseExcel(InputStream inputStream, String [] beanProperties,
            Class<?> [] propertyTypes, Class<T> clazz) throws Exception {
        if (beanProperties == null || propertyTypes == null) {
            throw new Exception("必须指定Excel列映射的JavaBean属性及相应的数据类型.");
        }
        if (beanProperties.length > propertyTypes.length) {
            throw new Exception("给定的JavaBean属性为" + beanProperties.length + 
                "个, 数据类型为" + propertyTypes.length + "个, 个数不匹配.");
        }
        Method [] setMethods = new Method [beanProperties.length];
        int i = 0;
        for(String property : beanProperties) {
            String setMethodName = "set" + property.substring(0, 1).toUpperCase() + 
                property.substring(1);
            try {
                setMethods[i] = clazz.getDeclaredMethod(setMethodName, propertyTypes[i++]);
            } catch (SecurityException se) {
                throw new Exception("属性" + property + "对应的setter方法可能无法访问.", se);
            } catch (NoSuchMethodException e) {
                String paramType = propertyTypes[--i].getName();
                throw new Exception("属性" + property + "没有参数类型为" + paramType + 
                    "的setter方法.", e);
            }
        }
        return parseExcel(inputStream, setMethods, propertyTypes, clazz);
    }
    
    /**
     * 具体解析方法, 由公共方法准备好数据后调用, 因此不公开.
     * @param inputStream 准备解析的Excel文件对应的输入流
     * @param setMethods setter方法数组, 与给定列顺序一样
     * @param propertyTypes JavaBean属性类型数组
     * @param cls JavaBean类的Class对象
     * @return 解析成功后JavaBean集合
     * @throws Exception
     */
    private static <T> List<T> parseExcel(InputStream inputStream, Method [] setterMethods, 
            Class<?> [] propertyTypes, Class<T> beanType) throws Exception {
        Workbook workbook = Workbook.getWorkbook(inputStream);
        Sheet sheet = workbook.getSheet(0);
 
        List<T> parseResultList = new ArrayList<T>(sheet.getRows() - 1);
        for(int row = 1; row < sheet.getRows(); row++) {    // 跳过第0行, 一般第0行是列名
            T instance = beanType.newInstance();    // 创建实例
            Cell [] cells = sheet.getRow(row);
            for(Cell cell : cells) {
                if (cell.getColumn() >= setterMethods.length) break;
                Method setMethod = setterMethods[cell.getColumn()];
                Class<?> type = propertyTypes[cell.getColumn()];
                if (null != setMethod) {
                    // 调用新创建实例的setter
                    invokeSetterMethod(setMethod, instance, cell, type);
                }
            }
            parseResultList.add(instance);
        }
        workbook.close();
        return parseResultList;
    }
    
    /**
     * 取出Excel单元格中的数据并作相应转换, 然后调用实例的设置属性值方法(setters), 给实例设置指定值
     * @param setterMethod setter方法
     * @param o setterMethod调用所针对的实例
     * @param cell Excel单元格
     * @param paramType setter方法所需参数的类型
     * @throws Exception
     */
    private static void invokeSetterMethod(Method setterMethod, Object o, Cell cell,
            Class<?> paramType) throws Exception  {
        try {
            if (cell instanceof EmptyCell) {    // 单元格没有内容, 将实体类相应属性设置为null
                setterMethod.invoke(o, new Object [] { null });
            } else if (String.class.isAssignableFrom(paramType) || char.class.isAssignableFrom(paramType)
                    || Character.class.isAssignableFrom(paramType)) {
                // String|char|Character设置为String
                setterMethod.invoke(o, cell.getContents());
            } else if (Double.class.isAssignableFrom(paramType) || double.class.isAssignableFrom(paramType)) {    // Double|double
                Double number = ((NumberCell) cell).getValue();
                setterMethod.invoke(o, number);
            } else if (Integer.class.isAssignableFrom(paramType) || int.class.isAssignableFrom(paramType)) {        // Integer|int
                Double number = ((NumberCell) cell).getValue();
                setterMethod.invoke(o, number.intValue());
            } else if (Long.class.isAssignableFrom(paramType) || long.class.isAssignableFrom(paramType)) {        // Long|long
                Double number = ((NumberCell) cell).getValue();
                setterMethod.invoke(o, number.longValue());
            } else if (Float.class.isAssignableFrom(paramType) || float.class.isAssignableFrom(paramType)) {        // Float|float
                Double number = ((NumberCell) cell).getValue();
                setterMethod.invoke(o, number.floatValue());
            } else if (Short.class.isAssignableFrom(paramType) || short.class.isAssignableFrom(paramType)) {        // Short|short
                Double number = ((NumberCell) cell).getValue();
                setterMethod.invoke(o, number.shortValue());
            } else if (Boolean.class.isAssignableFrom(paramType) || boolean.class.isAssignableFrom(paramType)) {    // Boolean|boolean
                setterMethod.invoke(o, ((BooleanCell)cell).getValue());
            } else if (Date.class.isAssignableFrom(paramType)) {    // java.util.Date
                setterMethod.invoke(o, ((DateCell)cell).getDate());
            } else {
                throw new Exception("方法" + setterMethod.getName() + 
                    "所需要的参数类型不被支持.");
            }
        } catch (IllegalArgumentException e) {
            throw new Exception("setter方法" + setterMethod.getName() + "需要参数的类型为" + 
                    setterMethod.getParameterTypes()[0].getName() + 
                    ", 传入的类型为" + paramType.getName(), e);
        }
    }
}

用Java时间也不短了,但对其一些高级特性总是不太明白,这其中就包括反射和泛型;也许有人觉得泛型不算高级特性,因为我们经常会接触到它,但我们真的理解它了吗?看《Thinking In Java》的时候略有所悟,但放下书本后又觉得还是不明白,也许经常写一些类似这种的通用代码,对它们的理解会更深一点儿...

转载自https://blog.csdn.net/zhyh1986/article/details/8511186

使用Java泛型和反射机制编写Excel文件生成和解析的通用工具类相关推荐

  1. 深入理解Java中的反射机制和使用原理!详细解析invoke方法的执行和使用

    反射的概念 反射:Refelection,反射是Java的特征之一,允许运行中的Java程序获取自身信息,并可以操作类或者对象的内部属性 通过反射,可以在运行时获得程序或者程序中的每一个类型的成员活成 ...

  2. java 反射与泛型_Java基础系列 - 泛型和反射机制

    package com.test5; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Java泛型和反射机 ...

  3. java如何用反射把具体方法抽象_如何在Java 中使用泛型或反射机制对DAO进行抽象...

    如何在Java 中使用泛型或反射机制对DAO进行抽象 发布时间:2020-11-26 16:07:42 来源:亿速云 阅读:80 作者:Leah 本篇文章为大家展示了如何在Java 中使用泛型或反射机 ...

  4. formdata 接受参数中带有class 对象_浅析JAVA中的反射机制及对Servlet的优化

    今天来聊聊java中的反射机制,工作以后发现很多东西动不动就要使用反射或者动态代理,如果不能很好的理解反射,那么对于动态代理等一些重要的设计模式就会有种不够通透的感觉. 所谓的反射,就是在运行状态中, ...

  5. 浅说Java中的反射机制(一)

    在学习传智播客李勇老师的JDBC系列时,会出现反射的概念,由于又是第一次见,不免感到陌生.所以再次在博客园找到一篇文章,先记录如下: 引用自java中的反射机制,作者bingoideas.(()为我手 ...

  6. JAVA基础--JAVA中的反射机制详解

    JAVA反射机制     JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能 ...

  7. java 有哪些反射机制_Java 的反射机制你了解多少?

    不知道多少次听说过了Java反射机制的使用,比如:Spring 框架如何实例化IoC容器中的Bean,编码过程中如何动态的清理对象中的字段信息等等.工作中只是听说.看同事们编码实践,但是自己却只是概念 ...

  8. java代码安全检测机制_全面解析:java中的反射机制,内含代码验证解析

    什么是反射? 在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功 ...

  9. 根据实例详解Java中的反射机制

    概念: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java ...

最新文章

  1. 解决github push错误403 Forbidden while accessing
  2. VTK:可视化算法之DecimateFran
  3. NC88-寻找第K大的数
  4. c++制表符_在Linux命令行中将制表符(tab)转换为空格
  5. python学习第22天
  6. 计算机网络学习笔记-目录(更新日期:2020.4.8)
  7. 操作系统学习总结(超赞!!!)
  8. Power BI Desktop 10月更新
  9. 【狂神Mybatis笔记】配置解析
  10. Java习题>|异常>|throw自定义异常小案例
  11. python函数定义和调用练习_python函数的定义和调用
  12. Golang优秀开源项目汇总
  13. 数据安全生命周期管理介绍(一)
  14. 排球分组循环交叉编排_全国气排球邀请赛在我市举行
  15. 如何用Amira分割DICOM图像
  16. UNCTF2020web方向部分题解
  17. 没有备份电脑照片怎么恢复?分享3种找回照片方法
  18. app2sd 与 A2SD+
  19. 程序员的表白: 教你制作一个百分之99成功率的表白网站 (html+css+js)
  20. 区块链革命 - 第2篇 转型 - 第3章 重塑金融服务形象:从赚钱机器变成致富平台

热门文章

  1. freertos 创建互斥量_freertos任务通信
  2. python中request方法_如何使用python语言中的request模块获取代码
  3. python开发环境推荐_推荐一款Python开发环境管理神器
  4. c语言指针填空题目,C语言指针题目实战
  5. k8s kubectl生成kube-config文件
  6. 深度学习之正则化方法
  7. CF935D Fafa and Ancient Alphabet 概率dp(递推)
  8. 安卓系统为何这么容易被黑客入侵
  9. 刚刚收到的邮件,Google对Admob的收购已经顺利结束
  10. 李开复系列--成功、自信、快乐