专栏首页瞎说开发那些事Java基于注解和反射导入导出Excel

Java基于注解和反射导入导出Excel

1. 构建项目

使用Spring Boot快速构建一个Web工程,并导入与操作Excel相关的POI包以及一些常用的工具类包,pom文件中添加如下一些依赖:

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.3.2</version>
        </dependency>
        <dependency>
            <groupId>com.google.collections</groupId>
            <artifactId>google-collections</artifactId>
            <version>1.0</version>
        </dependency>

2. 自定义Excel注解

使用注解的形式,自定义一些与操作Excel相关的基本信息,如生成Excel模板时,需要有哪些字段名、字段标题、字段之间的排序、字段中内容的位置、对齐方式等信息。然后通过在JavaBean中的需要的字段对应的getter方法上添加这些注解,就可以将其标记为Excel相关的字段。自定义注解内容主要如下(为了节省篇幅,一下代码中的注解已删除,详细代码可以看文章后面的链接)

@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelField {    
String value() default "";    
String title();    
int type() default 0;    
int align() default 0;    
int sort() default 0;    
String dictType() default "";
Class<?> fieldType() default Class.class;    
int[] groups() default {};
}

3. 通过反射创建Excel模板

使用反射的方式获取类的信息、类中方法、属性,为了创建一个可供用户填写的Excel模板,我们需要在模板中定义系统需要收集的数据字段,即在JavaBean中,通过注解定义的相关字段。伪代码及关键代码如下:

 public ExportExcel(String title, Class<?> cls, int type, int... groups){        
        // Get annotation field
        Field[] fs = cls.getDeclaredFields();        

 for (Field f : fs){            
            //获取字段上加的@Excel注解
            ExcelField ef = f.getAnnotation(ExcelField.class);             
            if (ef != null && (ef.type()==0 || ef.type()==type)){            
            //根据字段注解中配置的groups进行分组
            //....
            }else{            
                //若无group属性,则直接将字段和对应的注解加入到一个全局的注解链表中,用于之后进行统一的排序
                 annotationList.add(new Object[]{ef, f});
                }
            }
        }        

         // Get annotation method
        Method[] ms = cls.getDeclaredMethods();               

         for (Method m : ms){                     

            //获取方法上的注解
            ExcelField ef = m.getAnnotation(ExcelField.class);            

                 if (ef != null && (ef.type()==0 || ef.type()==type)){              

             //操作同对字段的操作
                }else{
                    annotationList.add(new Object[]{ef, m});
                }
            }
        }        
        // 对字段进行排序
        Collections.sort(annotationList, new Comparator<Object[]>() {         
‍                ‍//排序规则
        });        
        // Initialize
        List<String> headerList = Lists.newArrayList();        

         for (Object[] os : annotationList){            
            //获取注解title属性值
            String t = ((ExcelField)os[0]).title();            
            //将字段名称保存在一个list中,交给初始化方法使用
            headerList.add(t);
        }        
        //初始化操作,创建Excel,设置文件名称,表格标题,表头内容及单元格的格式等信息
        initialize(title, headerList);
    }

4. 导入Excel文件

导入Excel文件,意味着需要将一个根据我们生成模板填好的Excel文件导入到系统中。在这个过程中,需要使用一个接口去接收文件,并对文件进行解析。在Excel文件中,每一行都对应着我们定义的一个实体对象,所以解析之后,我们得到的是一个存放着多个对象的List。 在解析文件的过程中,首先需要对文件格式校验,保证是一个有效的Excel文件,然后循环读取每一行的数据,并将其赋值给对象。

5. 导出Excel文件

导出Excel的原理同导出模板一样,只是需要将数据填充到Excel文件中。填充数据过程中,还是需要通过@Excel注解将JavaBean中的字段找出,并将值设置到单元格中

6. 测试

01

定义实体类

在实体类中为每个字段添加@Excel注解

public class User {    
private String userName;    
private String nickName;    
private Integer age;    
private Date birth;    

@NotNull(message = "User Name 不能为空")    
@ExcelField(title="User Name", align=2, sort=1)    
public String getUserName() {        
    return userName;
    }    
    
public void setUserName(String userName) {        
    this.userName = userName;
    }    
    
@ExcelField(title="Nick Name", align=2, sort=2)    
public String getNickName() {        
    return nickName;
    }    

public void setNickName(String nickName) {        
    this.nickName = nickName;
    }    

@ExcelField(title="Age", align=2, sort=3)    
public Integer getAge() {        
    return age;
    }    

public void setAge(Integer age) {        
    this.age = age;
    }    

@JsonFormat(pattern = "mm/dd/YYYY")   
@NotNull(message="Birth Day不能为空")    
@ExcelField(title="Birth Day", align=2, sort=4)    
public Date getBirth() {        
    return birth;
    }    

public void setBirth(Date birth) {        
    this.birth = birth;
    }
}

02

定义接口方法

1. 下载输入数据的模板
    @RequestMapping("import/template")    
    public void importFileTemplate(HttpServletResponse response){        
    try {            
            //定义文件名称
            String fileName = "User_Data_import_template.xlsx";
            List<User> list = Lists.newArrayList();            
            new ExportExcel("User Data", User.class, 1).setDataList(list).write(response, fileName).dispose();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
2. 导入Excel文件到系统
  @RequestMapping(value = "import",method = RequestMethod.POST)    
  public void importFile(MultipartFile multipartFile){        
      try {            
          int successNum = 0;            
          int failureNum = 0;        
          StringBuilder failureMsg = new StringBuilder();
            
          ImportExcel ei = new ImportExcel(multipartFile, 1, 0);
            
          List<User> list = ei.getDataList(User.class);            
          for (User user : list){                
              try{                    
                  //to do: 保存/处理数据
                  //userService.save(user);
                  logger.info(user.toString());
                  successNum++;
               }catch(ConstraintViolationException ex){
                   failureNum++;
               }catch (Exception ex) {
                   failureNum++;
               }
            }            
            if (failureNum>0){
                failureMsg.insert(0, ", Failures: "+failureNum);
            }
            logger.info("Had Operation "+successNum+" Data;"+" "+"Failure "+failureNum);
        } catch (Exception e) {
            logger.error("导入失败",e);
        }
    }
3. 导出Excel文件
 @RequestMapping("export")    
 public void export(HttpServletResponse response){        
     try {
            String fileName = "User Data"+ DateUtils.getDate("yyyyMMddHHmmss")+".xlsx";
            List<User> users=new ArrayList<>();
            User user1=new User();
            user1.setUserName("小明");
            user1.setNickName("猪小明");
            user1.setAge(20);
            user1.setBirth(DateUtils.parseDate("1992-10-10"));
            users.add(user1);
            User user2=new User();
            user2.setUserName("小红");
            user2.setNickName("小小红");
            user2.setAge(18);
            user2.setBirth(DateUtils.parseDate("1998-11-09"));
            users.add(user2);            
            new ExportExcel("Test Over View Define", User.class,2).setDataList(users).write(response, fileName).dispose();
        } catch (Exception e) {
        }
    }

03

演示

端口号可以自己通过在application.properties文件中,添加server.port=8000进行定义

通过浏览器访问接口http://localhost:8000/user/import/template,下载模板:

编辑Excel文件,并通过接口测试工具Postman访问接口localhost:8000/user/import

接口测试工具中,上传文件,并访问:

上传之后,通过日志输出文件解析的结果:

2017-11-24 19:56:15.186  INFO 37428 --- [nio-8000-exec-5] com.shexd.Controller.UserController      : User{userName='小明', nickName='猪小明', age=18, birth=1992-10-09}
2017-11-24 19:56:15.187  INFO 37428 --- [nio-8000-exec-5] com.shexd.Controller.UserController      : User{userName='蘑菇头', nickName='小蘑菇', age=21, birth=1996-09-25}
2017-11-24 19:56:15.187  INFO 37428 --- [nio-8000-exec-5] com.shexd.Controller.UserController      : Had Operation 2 Data; Failure 0

访问接口http://localhost:8000/user/export,从系统导出Excel文件

7. 小结

本文简单介绍了利用Java注解和反射对Excel进行操作的基本原理,并实例进行详细说明。本文中项目代码已上传至Github,别忘了Star一个吆,传送门:

ExcelHandle。

本文分享自微信公众号 - 瞎说开发那些事(jsj201501),作者:向东

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-11-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • [并发编程系列]Java中的原子操作类

    用户2017109
  • [微服务系列] 微服务构建框架--Spring Boot

    用户2017109
  • UiPath中调用 Abbyy Cloud OCR

    用户2017109
  • 第16章 非阻塞式I/O

    执行时间:         停等版本(完全阻塞)  》  select加阻塞I/O版本  》  fork多进程版本(Linux下多线程也应该差不多)  》  非...

    _gongluck
  • 002 C# 写入Excel,Word,Ppt完整攻略

    在项目-添加引用,分别选择Microsoft Excel 16.0 Object Library、

    职场编码
  • 微服务的异常处理

    不加班的周末,整理了一下项目上的异常处理方案,和小伙伴们共享,里面不成熟的代码或解决方式.QAQ,评论区走起

    3号攻城狮
  • 做数据只知道Excel?Jupyter Notebook也要学起来了

    如果你是一名交易员或者从事金融服务行业,那么 Excel 就是你的生计之本。有了它,你可以分析价格和实时数据、评估交易组合、计算 VaR、执行回测等等;有了它,...

    机器之心
  • Excel技巧:如何实现Excel计算错误,系统就提示错误?

    小伙伴的提的问题有很代表性,问如何判断Excel计算错误,系统就给提醒告诉我们Excel计算错了。当看到这个问题我都蒙了,这Excel还会计算错误的?计算错误了...

    砸漏
  • 计算机程序的思维逻辑 (第7节更新) - 再谈乱码恢复

    在第6节和第7节,我们讨论了文本的二进制编码、乱码、以及恢复,第6节受到了很多读者的一致好评,但第7节有读者反馈解说的不太透彻,希望再详细一点,本文就是对第7节...

    swiftma
  • Java|Java生成Excel表

    最近遇到一个问题,就是在系统页面上加一个Excel表格导出的问题,这个问题很好解决啊,写一个JS把后台给的数据导出到Excel表格不就行了吗。然而当我们在测试的...

    算法与编程之美

扫码关注云+社区

领取腾讯云代金券