您的位置:首页 > 编程语言 > Java开发

利用Java反射机制和POI实现导出Excel功能

2014-11-16 17:00 465 查看
##  1.Apache POI简介

Apache poi工具包是一个著名的操作Microsoft文档的Java工具库,里面提供大量的对word,excel,ppt操作的方法。最近由于项目需要一个将数据库数据导出到excel的功能,所以学习了这个工具包的用法。下面简要介绍一下操作excel的几个重要的类和方法。

### 1.HSSF、XSSF和SXSSF

  

HSSF和XSSF包都是poi中操作excel的包,他们的区别在于hssf包操作的是Excel '97(-2007)格式的文档,而xssf操作的是 Excel 2007 以上版本格式的文档。换言之,hssf生成的excel文件后缀名是.xls,xssf生成的文件后缀是.xlsx。如果要同时支持这两种格式的文档,poi也提供了SXSSF包来实现其功能。

### 2.创建一个excel文档

poi包在操作的时候有几个重要的概念,workbook,sheet,row,和cell.。这几个概念与excel中的同样的概念对应。一个workbook代表着一个完整的excel文档,它包含一个或多个sheet;一个sheet表示一个具体的表格,包含多个row对象,一个row就是表格的一行,包含多个单元格cell。用poi生成一个excel文档的示例如下:

```

public class CreateExcelSimpleTest {   
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个工作簿
Workbook wb = new SXSSFWorkbook();
//在该工作簿上创建一个表格并命名
Sheet sheet = wb.createSheet("POISimpleTest");
//在表格中创建一个行对象,也就是第1行,poi中行数是从0开始计算的
Row row = sheet.createRow(0);
//在行对象中创建第一个单元格对象,单元格也是从0开始计数
Cell cell = row.createCell(0);
cell.setCellValue("第一个单元格");
/**
* 上面的操作都是在内存中进行,声称表格需要输出到文件
*/
FileOutputStream out = null;
try{
out = new FileOutputStream("D:\\第一个poi表格.xls");
wb.write(out);
}catch(IOException e){
e.printStackTrace();
}finally{
if(out != null){
try {

                    out.close();

                } catch (IOException e) {

        e.printStackTrace();

                }
}
}
}

}

```

## 2.利用Java反射机制实现自适应导出功能

从上面的程序可以看出,仅仅使用poi包进行excel生成的话会很死板。因为创建行和单元格的时候都需要指定行号和列号。我们导出Excel的时候,一般会传入类似以List<XXXDO>的数据参数,每一行是一个DO。那么,如果我们需要程序适应多种DO数据参数时,如何来确定表格的列数和每一个字段所在的列号呢?我们可以使用Java的反射机制来实现这种自适应需求。

这种需求的关键是我们需要程序智能的识别出传入的每个DO对象有多少个需要展示的字段,以及表头是什么,如何按照表头的顺序展示每个DO中的字段。我们来看看,如何利用反射机制实现这个功能。

### 1)创建表头

每一个表格都需要表头来表明每一列的意义,表头一般会是具有意义的汉字或者英文;表头与DO之间的关联就是DO中的变量名。所以我们可以使用一个LinkedHashMap<String,String>结构来存储DO中的字段变量名和表头文字之间的映射,比如

```

LinkedHashMap<String,String> header = new LinkedHashMap<String,String>();

        header.put("recordTime", "时间");

        header.put("businessLine", "业务线");

        header.put("resourceName", "名称");        

```

使用LinkedHashMap是为了让表头的展示顺序能够受我们的控制,能够与hashmap中写入的顺序一致。

```

   /**

     * 创建各列表头

     * @param headString

     */

    public void createHeadRow(){    

        Row head = sheet.createRow(0);   //创建表格第一行对象,为表头行

        Iterator<Entry<String,String>> headTitle = header.entrySet().iterator();  //循环输出表头

        for(int i=0;headTitle.hasNext();i++){

            Cell cell = head.createCell(i);

            cell.setCellValue(headTitle.next().getValue());

        }

    }

```

### 2)利用反射获取DO的属性值

我们需要让程序知道传过来的DO有哪些属性,属性变量的类型是什么,以及如何获取属性的值,做到这样我们才能让程序自动将相应的值填写到表格里面。

首先定义一个Entry类,来存储每一个属性变量的名称、类型和值;

```

package poi.autoreflect;

/**

 * 类FieldsEntity.java的实现描述:TODO 类实现描述 

 * @author keming.hh 2014年9月24日 下午4:13:21

 */

public class FieldsEntity {

    private String attributeName;     //属性变量名称    

    private Object value;                //属性变量值    

    private Class classType;          //属性类型  

 

    public String getAttributeName() {

        return attributeName;

    }    

    public void setAttributeName(String attributeName) {

        this.attributeName = attributeName;

    }   

    public Object getValue() {

        return value;

    }    

    public void setValue(Object value) {

        this.value = value;

    }     

    public Class getClassType() {

        return classType;

    } 

    public void setClassType(Class classType) {

        this.classType = classType;

    }    

    public FieldsEntity(String fieldName, Object o, Class classType){

        this.attributeName = fieldName;

        this.value = o;

        this.classType = classType;

    }

    

}

```

对象转换,将每一个DO转换为上面的FieldsEntity 类形式,并存如map中;

```

/**

 * 类DataConvertUtil.java的实现描述:TODO 类实现描述  

 */

public class DataConvertUtil {

//    private static final Logger logger = LoggerFactory.getLogger(DataConvertUtil.class);

    

    /**

     * 将对象的属性名称与值映射为MAP

     * @param o 对象

     * @return  Map<key,value> key为属性名称,value为名称、类型和值组成的对象 

     */

    public static Map<String,FieldsEntity> convertObjectToMap(Object o){

        Class oClass = o.getClass();

        Field[] fields = oClass.getDeclaredFields();   //获取类中的所有声明的属性

        Map<String,FieldsEntity> map = new HashMap<String, FieldsEntity>();

        try{

            for(int i=0;i<fields.length;i++){

//                不对序列化ID进行映射

                if(fields[i].getName().equals("serialVersionUID")){   

                    continue;

                }

                Object valueObject = getFieldValue(o,fields[i].getName());

                map.put(fields[i].getName(), new FieldsEntity(fields[i].getName(), valueObject, fields[i].getType()));

            }

            return map; 

        }catch(Exception e){

           e.printStackTrace();           

        }     

    }   

}

```

从上面的代码可以看出,我们利用反射的机制获取每一个DO中的所有fied,同时利用getFieldValue方法获取每一个属性的值。那么这里getFieldValue方法做什么工作呢?其实他就是拼凑出DO中的所有getter方法名,然后调用getter方法来获得属性值。当然,这种情况只有在DO的getter方法是符合规范的命名的情况下才能有效,由此可见命名规范多么重要啊。

```

 /**

     * 通过对象的getter方法获取属性值

     * @param o 对象

     * @param name 属性名称

     * @return 相应属性的值 

     */

public static Object getFieldValue(Object o,String name) throws SecurityException, NoSuchMethodException,IllegalArgumentException, IllegalAccessException, InvocationTargetException {

        Class owner = o.getClass();

        Method mothed = owner.getMethod(createGetter(name));

        Object object = mothed.invoke(o);

        return object;

    }

    

    /**

     * 通过属性名称拼凑getter方法

     * @param fieldName

     * @return

     */

    public static String createGetter(String fieldName){

        if(fieldName == null || fieldName.length() == 0 ){

            return null;

        }        

        StringBuffer sb = new StringBuffer("get");

        sb.append(fieldName.substring(0, 1).toUpperCase()).append(fieldName.substring(1));

        return sb.toString();

    }

```

通过上面的步骤,我们就可以让程序获得任何一个DO的属性数量,属性值,然后我们就可以自动的进行Excel生成了。

### 3)生成表格

```

 /**

     * 创建数据行

     * @param data

     * @param cols

     */

    public static void createRows(List<?> data){

    int rowCount = data.size();     //根据数据集设置行数

    for(int i=0;i<rowCount;i++){

            Row row = sheet.createRow(i+1);  //创建行,表头是第0行

           //转换数据,将每一个DO映射为属性名与FieldsEntity的Map

            Map<String,FieldsEntity> map = DataConvertUtil.convertObjectToMap(data.get(i));            

            Iterator<Entry<String,String>> head = header.entrySet().iterator();

            //创建每行的单元格并填充值

            for(int col = 0;col < header.size() && head.hasNext();col++){

                Cell cell = row.createCell(col);

                //设置表头的迭代器

                Map.Entry<String,String> enty = (Map.Entry<String,String>)head.next();

                String name = enty.getKey();

                cell.setCellValue(map.get(name).getValue().toString());        //填充属性值      

            }

        }        

    }

```

### 4)将excel输出到文件流

```

 private static void flashoutFile(OutputStream out, SXSSFWorkbook book) {

        try {

            book.write(out);

            out.close();

            book.dispose();

        } catch (IOException e) {

            e.printStackTrace();

        } finally{

            if(out!=null){

                try {

                    out.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        }

    }

```

到此我们这个功能就实现了,可以让程序自动识别DO的需要展示的属性,并生成Excel文档。测试的代码如下

测试数据DO :

```

public class DataObject {
private Date recordTime;
private String businessLine;
private String resourceName;
public Date getRecordTime() {
return recordTime;
}
public void setRecordTime(Date recordTime) {
this.recordTime = recordTime;
}
public String getBusinessLine() {
return businessLine;
}
public void setBusinessLine(String businessLine) {
this.businessLine = businessLine;
}
public String getResourceName() {
return resourceName;
}
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}

}

```

整体测试程序:

```

public class CreateExcelByReflect {
private static Sheet sheet;    //表格对象   
private static SXSSFWorkbook wb;    //工作簿

        private static LinkedHashMap<String,String> header;   //表头    

//此处省略了功能模块代码,功能时间参见上面的介绍代码

    //初始化

    public static void init(String sheetName) {

        wb = new SXSSFWorkbook();

        sheet = wb.createSheet(sheetName);

    header = new LinkedHashMap<String,String>();

        header.put("recordTime", "时间");

        header.put("businessLine", "业务线");

        header.put("resourceName", "名称");

        

    }       
public static void main(String[] args) {
init("测试表格");        

        Date date = new Date();

        DataObject data1 = new DataObject();

        data1.setRecordTime(date);

        data1.setBusinessLine("业务1");

        data1.setResourceName("资源1");

        

        DataObject data2 = new DataObject();

        data2.setRecordTime(date);

        data2.setBusinessLine("业务2");

        data2.setResourceName("资源2");

        

        List<DataObject> data = new ArrayList<DataObject>();

        data.add(data1);

        data.add(data2);

        

        createHeadRow(sheet);   //创建表头

        createRows(data);           //创建数据行

        try{

        FileOutputStream out = new FileOutputStream("D:\\测试2.xlsx");

        flashoutFile(out,wb);

        }catch(Exception e){

        e.printStackTrace();

        }       
}

}

```
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java POI