基于Java反射机制的POI实现Excel数据导入/导出

2015-04-29 00:44朱文君黄国权
计算机时代 2015年1期

朱文君 黄国权

摘  要: 随着Java语言越来越多地被选择用于B/S结构系统的开发语言,利用POI解析技术操作Excel文件越来越广泛。在POI组件下,采用Java反射机制及自定义注解原理,设计实现了数据库与Excel文件的数据交互。此方法不仅能保证数据导入的完整性,而且能免去数据导出后重新编辑的复杂性,从而提高POI实现Excel数据导入/导出的灵活性、重用性和易扩展性。

关键词: POI; Excel; 反射机制; 自定义注解; 数据导入导出

中图分类号:TP312          文献标志码:A     文章编号:1006-8228(2015)01-38-02

Realization of import and export of data in Excel files by POI based on Java reflection

Zhu Wenjun, Huang Guoquan

(College of Medical Information Engineering, Guangdong Pharmaceutical University, Guangzhou, Guangdong 510006, China)

Abstract: With more and more systems on B/S structure being developed by Java, operating Excel by POI is used more widely. Under POI component, applying Java reflection and custom annotation, data interaction between database and Excel files is designed and realized. This method can not only ensure the integrity of data import, but also avoid the complexity of the data export. Flexibility, re-usability and ductility of using POI in importing and exporting Excel files are improved.

Key words: POI; Excel; reflection; custom annotation; data import and export

0 引言

在Web应用系统中,用户常会要求将数据库中的数据导出到Excel表格中,或将Excel表格中的数据导入到数据库中[1]。然而,传统的Excel数据导入导出技术,对于不同的对象,都需要重新配置固定的表头并且频繁更改关键代码算法,导致程序员操作过于繁琐。本文介绍一种基于Java反射机制原理,只需要配置自定义注解,而无需更改关键代码算法的Excel导入导出技术。

1 Java反射机制简介

Java反射机制是指Java语言在运行时拥有的一项自审的能力,对自身进行检查,并能直接操作程序的内部属性[2]。即在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。通过采用该机制来实现对自己行为的描述和检测,并能根据自身行为的状态和结果,为下一步的动作做准备[3]。

也就是说,Java反射机制提供了一种在运行中获得类信息并构建类的Class对象和生成类的实例的机制。同时,使程序代码能够访问装载到JVM中的类的内部信息,主要包括:已装载类的字段、方法和构造函数的信息,并允许编写处理类的代码[4]。

2 辅助类

2.1 ExcelAnnotation.java

@Retention(RetentionPolicy.RUNTIME)

public @interface ExcelAnnotation {

String title();

int order();

}

ExcelAnnotation注释采用自定义注解技术,可在实体model类上的字段get()方法上注释,同时设定属性所对应的title(标题)、order(顺序)。通过设定@Retention(RetentionPolicy.RUNTIME),该注释会在Class字节码文件中存在,在运行时可通过反射机制获取该注释的属性。没有注释的实体model类的字段将不受影响,有注释的实体model类的字段将会根据title的值输出标题,并且根据order的值进行排序。从而在不需要预定义模板的情况下,实现Excel表格的表头的动态输出,提高了功能模块的灵活性。

2.2 ExcelHeader.java

public class ExcelHeader implements Comparable

{

private String title;

private int order;

private String methodName;

……

public int compareTo(ExcelHeader eh) {

return order>eh.order?1:(order

public ExcelHeader(String title, int order,

String methodName) {

super();

this.title = title;

……

}

}

ExcelHeader类用来存储Excel标题的对象,通过该类在反射机制中可以动态获取标题和方法的对应关系。同时,该类实现Comparable接口,重写了public int compareTo()方法。通过调用Collections.sort()方法,可根据order的值进行标题的排序,从而实现Excel表格的表头的灵活配置。

3 主类

3.1 Excel数据导出

传统的Excel数据导出技术,只能根据数据持久层或业务逻辑层中已经由程序员写好的sql查询语句导出规定的数据,不能满足用户的自定义需求。引用Java反射机制后,通过BeanUtils.getProperty()方法,根据加载类对象和字段名便可以动态获得对应的属性值,因此,不需要更改关键代码的算法,减少了程序员的操作,同时也提高了功能模块的重用性。

以下为Excel数据导出的关键代码:

private List getHeaderList(Class model) {

List headers=new ArrayList();

//通过反射机制,能访问model对象表示的类的所有方法

Method[] methods=model.getDeclaredMethods();

for (Method m:methods) {

String mn=m.getName();

if (mn.startsWith("get") ) {

//通过反射机制,判断注释存在于方法上,筛选出指定的数据集

if (m.isAnnotationPresent(ExcelAnnotation.class)) {

ExcelAnnotation ea=m.getAnnotation(ExcelAnnotation.class);

headers.add(new ExcelHeader(ea.title(),ea.order(), mn));

}

}

}

return headers;

}

getHeaderList()方法首先通过反射机制,获取实体model类的所有方法,然后通过自定义注解技术获取存在于方法上的注释,并且把注释的内容添加到headers对象中,从而动态的绑定标题和方法的对应关系。

private Workbook exportExcel(List objs, Class model) {

……

List headers=getHeaderList(model);

Collections.sort(headers);

//输出表头

for (int i=0; i

cell1.setCellValue(headers.get(i).getTitle()); }

//输出对象信息

for (int i=0; i

……

obj=objs.get(i);

for (int j=0; j

Cell cell2=r.createCell(j);

setDefaultCellStyle(cell2, cellStyle);

//通过反射机制,调用加载类对象和字段名获取对应的属性值

cell2.setCellValue(BeanUtils.getProperty(obj,

getMethodName(headers.get(j))));

……

}

exportExcel()方法通过调用getHeaderList()取得headers对象中标题和方法的对应关系。通过反射机制,调用加载类对象和字段名获取对应的属性值,从而动态的绑定表头和属性值的映射关系。最后,通过Workbook对象传值调用POI生成Excel表格方法,便实现将数据库中的数据导出到Excel表格中。

3.2 Excel数据导入

传统的Excel数据导入技术,需要程序员在数据持久层或业务逻辑层中手动组合sql插入语句。数据处理效率过低,并且容易导致数据处理出错。引用Java反射机制后,通过BeanUtils.copyProperty()方法,把字段名和对应的属性值复制到加载类对象中,调用数据持久层的存储操作即可。同样,不需要更改关键代码的算法,体现了功能模块的重用性。

以下为Excel数据导入的关键代码:

private Map getHeaderMap(Row titleRow,

Class model) { List headers=getHeaderList

(model);

for (Cell c:titleRow) {

String title=c.getStringCellValue();

for (ExcelHeader eh : headers) {

if (eh.getTitle().equals(title.trim())) {

maps.put(c.getColumnIndex(), eh.getMethodName()

.replace("get", "set"));

……

return maps; }

getHeaderMap()方法首先获取Excel表格中的表头,然后通过与实体model类中注释的标题作对比,若一致,则把标题的顺序和对应的方法名添加到maps对象中。

public List readExcel(Workbook wb, Class model,

int read, int tail) { ……

List objs=null;

objs=new ArrayList();

Map maps=getHeaderMap(row, model);

if (maps==null || maps.size()<=0) throw new

RuntimeException("要读取的Excel表格的格式不正确,

请检查标题栏顺序!");

for (int i=read+1; i<=sheet.getLastRowNum()-tail; i++) {

row=sheet.getRow(i);

//通过反射机制,只能调用无参数的构造方法,筛选出指定的数据集

Object obj=model.newInstance();

for (Cell c:row) {

int ci=c.getColumnIndex();

//对方法名进行改造,形成字段名

String mn=maps.get(ci).substring(3);

mn=mn.substring(0, 1).toLowerCase()+mn.substring(1);

Map params=new HashMap

Object>(); //通过反射机制,把字段名和对应的属性值复制到加载类对象中

BeanUtils.copyProperty(obj, mn, this.getCellValue(c)); }

…… }

readExcel()方法通过调用getHeaderMap()取得Excel表格中的标题和对应实体model的方法名的对应关系。通过反射机制,把字段名和Excel表格中对应的属性值复制到obj对象中,形成实体model类的复制类。最后,通过objs集合传值调用数据持久层的存储操作,实现将Excel表格中的数据导入到数据库中。

4 结束语

反射是Java语言中一个非常突出的动态相关机制。在使用Java语言开发出灵活、高重用及易于扩展的系统的过程中,反射机制起到越来越关键的作用[5]。本文通过对基于Java反射机制的POI实现Excel数据导入导出的研究,减少了程序员的手工操作,带来了极大的方便。该方法在实际中得到应用,取得了较好的效果。

本文创新点:第一,在Web应用系统中,只需要本文中所提及的三个封装类,即ExcelAnnotation注释、ExcelHeader类及ExcelUtil类便可以实现其功能,体现了功能模块的松耦合性;第二,只需为实体类配置ExcelAnnotation注释,在不需要预定义模板的情况下,实现Excel表格的表头的动态输出,体现了功能模块的灵活性;第三,在实体类没有外键关联的情况下,不需要更改关键代码的算法,体现了功能模块的重用性。当实体类存在外键关联时,只需调用数据持久层的查询操作修改属性值即可,体现了功能模块的易扩展性。

参考文献:

[1] 戴维.POI实现Excel的数据导入导出的研究[J].科技信息,2013.1:

107

[2] Bruce Eckel.Thinking in Java[M].4.American:Prentice Hall PTR,

2006.

[3] 王善发,吴道荣.Java语言的反射机制[J].保山学院学报,2011.5:32

[4] 王开,谭翼,周兰江.Java中反射机制浅析及应用[J].计算机教育,

2007.1:255

[5] 尹松强,傅鹂.Java反射机制探究[J].软件导刊,2008.7(11):85