通过反射动态解析实例对象

代码背景

1、人员信息导出为Excel的时候,简化操作步骤,直接通过解析数据对象Do的成员变量作用域来生成Excel的数据
2、人员信息Excel导入的时候,直接通过成员变量名逆向拼装获取Set方法,自动封装数据对象Do,以便保存到数据库中
3、以上操作的复用,针对不同类别的人员,相似的操作可以通过修改极少量的代码来实现功能

一、利用反射解析数据对象Do成员变量(PrisonMemberDo)

/**
  * 导出
  *
  * @param memberQo
  * @return
  */
 @Override
  public String export(PrisonMemberQo memberQo) throws BusinessException {

      final List<PrisonMemberDo> prisonMemberDoList = prisonMemberMapper.query(memberQo);

      final List<List<String>> exportList = getExportList(prisonMemberDoList);
      final String folderPath = propertyConfig.get("export.url", "/icooper/appdata/hbsf/export/");
      final String fileName = ExportConstants.FILE_NAME_PRISON_MEM + ExportConstants.UNDER_LINE + TimeUtils.currTime1()
              + SCConstants.FMT_XLS;
      final String filePath = folderPath + fileName;
      final ExportExcel ex = new ExportExcel(ExportConstants.TITLE_PRISON_MEM, ExportConstants.HEADER_PRISON_MEM, exportList,
              folderPath, fileName);
      try {
          ex.export();
      } catch (final Exception e) {
          log.error("服刑人员导出失败", e);
          throw new BusinessException("服刑人员导出失败");
      }
      return filePath;
  }

//省略部分代码...

/**
   * 导出数据解析
   *
   * @param dataList
   * @return
   */
  private List<List<String>> getExportList(List<PrisonMemberDo> dataList) {
      final List<List<String>> exportList = new ArrayList<List<String>>();
      int rowNo = 0;
      for (final PrisonMemberDo prisonMemberDo : dataList) {
          final List<String> rowData = new ArrayList<String>();
          rowNo += 1;
          rowData.add(String.valueOf(rowNo));
          autoFillRowData(rowData, prisonMemberDo, PrisonMemberDo.class);
          exportList.add(rowData);
      }
      return exportList;
  }

  /**
   * 利用反射批量解析字段
   *
   * @param rowData
   * @param o
   * @param c
   */
  private void autoFillRowData(List<String> rowData, Object o, Class<PrisonMemberDo> c) {
      Field[] fields = c.getDeclaredFields();
      // 循环遍历字段,获取字段相应的属性值
      for (Field field : fields) {
          // 假设不为空。设置可见性,然后返回
          field.setAccessible(true);
          try {
              // 设置字段可见,就可以用get方法获取属性值。
              if (!field.getName().equals("id")) {
                  //特殊字段处理数据库中保存的是int,根据字典表切换为对应汉字
                  if (field.getType() == Date.class) {
                      rowData.add(sdf.format(new Date(field.get(o).toString())));
                  } else if (codeMap.get(field.getName()) != null) {                    
                      CodeDo code = codeMapper.parserCodeName(codeMap.get(field.getName()), field.get(o).toString());
                      rowData.add(code == null ? "未定义" : code.getCodeName());
                  } else {
                      rowData.add(field.get(o).toString());
                  }
              }
          } catch (Exception e) {
              log.error("反射获取导出对象的成员域失败!", e);
          }
      }
  }

二、利用反射动态封装数据对象Do成员变量(PrisonMemberDo)

/**
  * 人员导入
  *
  * @param file
  * @return
  */
 @Override
 public Integer upload(MultipartFile file) {
     Sheet sheet = null;
     try {
         sheet = POIUtils.parseSheet(file, 0);
     } catch (final java.io.IOException e) {
         e.printStackTrace();
     }
     int num = 0;
     for (int i = 3; i <= sheet.getLastRowNum(); i++) {
         final Row row = sheet.getRow(i);
         if (null != row && row.getCell(0) != null && StringUtils.notEmpty(row.getCell(0).toString())) {
             num = parserExcel(row, num);
         }
     }
     return num;
 }
/**
  * 解析Excel文件
  *
  * @param row
  * @param num
  * @return
  */
  private int parserExcel(Row row, int num) {
      final PrisonMemberDo memberDo = new PrisonMemberDo();
      try {
          final String name = POIUtils.parseStr(row.getCell(2));
          final String memCode = POIUtils.parseStr(row.getCell(1));
          if (StringUtils.isEmpty(memCode) || StringUtils.isEmpty(name)) {
              return num;
          }
          if(prisonMemberMapper.selectSameCode(memCode) != null){
              return num;
          }
          Field[] fields = memberDo.getClass().getDeclaredFields();
          int cellIndex = 0;
          String cellVal = "";
          // 循环遍历字段,获取字段相应的属性值
          for (Field field : fields) {
              // 假设不为空。设置可见性,然后返回
              field.setAccessible(true);
              cellVal = POIUtils.parseStr(row.getCell(cellIndex));
              try {
                  // 设置字段可见,就可以用get方法获取属性值。
                  if (!field.getName().equals("id")&&!field.getName().equals("updatetime")) {
                      //特殊类型特殊处理 Date的日期格式限定为'yyyy-MM-dd',字典表汉字向数字转化
                      if (field.getType() == Date.class) {
                          getSetMethod(memberDo.getClass(), field.getName()).invoke(memberDo, new Object[]{sdf.parse(cellVal)});
                      } else if (codeMap.get(field.getName()) != null) {
                          CodeDo code = codeMapper.parserCodeVal(codeMap.get(field.getName()), POIUtils.parseStr(row.getCell(cellIndex)));
                          if(code!=null){
                              getSetMethod(memberDo.getClass(), field.getName()).invoke(memberDo, new Integer[]{Integer.parseInt(code.getCode().toString())});
                          }
                      } else {
                          getSetMethod(memberDo.getClass(), field.getName()).invoke(memberDo, new Object[]{cellVal});
                      }
                  }
              } catch (Exception e) {
                  log.error("反射获取并解析对象的成员域失败!", e);
              }
              cellIndex++;
          }
          memberDo.setUpdatetime(new Date());
          prisonMemberMapper.insertSelective(memberDo);
          num++;
          return num;
      } catch (final Exception e) {
          log.error("解析Excel表格失败!", e);
          return num;
      }
  }
/**
   * 根据成员变量名获取对应set方法
   * @param objectClass
   * @param fieldName
   * @return
   */
  public static Method getSetMethod(Class objectClass, String fieldName) {
      try {
          Class[] parameterTypes = new Class[1];
          Field field = objectClass.getDeclaredField(fieldName);
          parameterTypes[0] = field.getType();
          StringBuffer sb = new StringBuffer();
          sb.append("set");
          sb.append(fieldName.substring(0, 1).toUpperCase());
          sb.append(fieldName.substring(1));
          Method method = objectClass.getMethod(sb.toString(), parameterTypes);
          return method;
      } catch (Exception e) {
          e.printStackTrace();
      }
      return null;
  }

备注:
1、成员变量的动态解析需要注意Excel的头部数据定义顺序和成员变量顺序是否一致,如果不一致,会导致数据封装异常!对于自定义的字段需要跨过(like:序号)
2、对于占位符和标题需要跨过,cell的下标起始点应与实际Excel中有效数据保持一致,如果Excel中未展示数据库中所有字段,需根据成员变量名做过滤。

Excel头部定义
public static final String FILE_NAME_PRISON_MEM = "司法厅服刑人员信息";
public static final String TITLE_PRISON_MEM = "司法厅服刑人员信息";
public static final String[] HEADER_PRISON_MEM = new String[] { "序号", "编码","姓名","组织编码","部门名称","性别","民族","出生日期", "证件类型","证件号码","文化程度", "原政治面貌",
        "户籍所在地","固定居住地","家庭电话","移动电话","婚姻状况","判决书号","判决机关","判决日期","罪名","刑种","原判刑期","刑期起始日期","刑期结束日期","附加刑",
        "刑期变动","曾受惩处","主要联系人","主要联系人地址","主要联系人电话","相互关系","备注", "更新时间" };
数据对象Do
package cn.showclear.www.pojo.base;

import java.util.Date;

public class PrisonMemberDo {
    /** ID */
    private Integer id;

    /** 编码(可唯一标识) */
    private String code;

    /** 姓名 */
    private String name;

    /** 组织编码 */
    private String orgCode;

    /** + 部门名称 */
    private String  deptName;

    /** 性别(1:男 2“女) */
    private Integer sex;

    /** 民族 */
    private String nation;

    /** 出生日期 */
    private Date birthDate;

    /** 证件类型,包括1(居民身份证)、2(军人身份证件)、3(武警身份证件)、9(其他)。 */
    private Integer paperType;

    /** 证件号码 */
    private String paperNumber;

    /** 文化程度(1:文盲 2:小学 3:初中 4:高中(含中专,技校)5:大专  6:本科  7:硕士  8 :博士  9:其他) */
    private Integer degreeType;

    /** 原政治面貌(1:中共党员 :2:中共预备党员 3:共青团员 :4:民革党员 5:民盟盟员 6:民建会员 7:民进会员 8:农工党党员 9:致公党党员 10:九三学社社员 11:台盟盟员 12:群众) */
    private Integer politicalStatusOld;

    /** 户籍所在地 */
    private String registerAddress;

    /** 固定居住地 */
    private String fixedAddress;

    /** 家庭电话 */
    private String homePhone;

    /** 移动电话 */
    private String mobilePhone;

    /** 婚姻状况(1:未婚 2:已婚 3:离异 4:丧偶) */
    private Integer maritalStatus;

    /** 判决书号 */
    private String sentenceNumber;

    /** 判决机关 */
    private String sentenceUnit;

    /** 判决日期 */
    private Date sentenceDate;

    /** 罪名 */
    private String charge;

    /** 刑种(1:死缓 2:无期 3:有期 4:拘役 5:管制) */
    private Integer punishmentKind;

    /** 原判刑期 */
    private String prisonTerm;

    /** 刑期起始日期 */
    private Date prisonBeginDate;

    /** 刑期结束日期 */
    private Date prisonEndDate;

    /** 附加刑 */
    private String punishmentOther;

    /** 刑期变动 */
    private String punishmentChange;

    /** 曾受惩处 */
    private String punished;

    /** 主要联系人 */
    private String mainContactName;

    /** 主要联系人地址 */
    private String mainContactAddr;

    /** 主要联系人电话 */
    private String mainContactTel;

    /** 相互关系 */
    private String interrelation;

    /** 备注 */
    private String remark;

    /** 更新时间 */
    private Date updatetime;


    public PrisonMemberDo() {
    }

    //...get && set

}

三、反射实例

遍历成员变量

/**
  * 利用反射批量解析字段
  *
  * @param rowData
  * @param o
  * @param c
  */
  private void autoFillRowData(List<String> rowData, Object o, Class<PrisonMemberDo> c) {
      Field[] fields = c.getDeclaredFields();
      // 循环遍历字段,获取字段相应的属性值
      for (Field field : fields) {
          // 假设不为空。设置可见性,然后返回
          field.setAccessible(true);
          try {
              // 设置字段可见,就可以用get方法获取属性值。
              if (!field.getName().equals("id")) {
                  if (field.getType() == Date.class) {
                      rowData.add(sdf.format(new Date(field.get(o).toString())));
                  } else if (codeMap.get(field.getName()) != null) {
                      CodeDo code = codeMapper.parserCodeName(codeMap.get(field.getName()), field.get(o).toString());
                      rowData.add(code == null ? "未定义" : code.getCodeName());
                  } else {
                      rowData.add(field.get(o).toString());
                  }
              }
          } catch (Exception e) {
              log.error("反射获取导出对象的成员域失败!", e);
          }
      }
  }

获取Set和Get方法

/** 
  * java反射bean的get和set方法 
  *  
  * @param objectClass 
  * @param fieldName 
  * @param fnType "get" or "set"
  * @return 
  */  
  @SuppressWarnings("unchecked")  
  public static Method getFnMethod(Class objectClass, String fieldName,String fnType) {  
      StringBuffer sb = new StringBuffer();  
      sb.append(fnType);  
      sb.append(fieldName.substring(0, 1).toUpperCase());  
      sb.append(fieldName.substring(1));  
      try {  
          return objectClass.getMethod(sb.toString());  
      } catch (Exception e) {  

      }  
      return null;  
  }  

执行set或get方法

/** 
  * 执行方法 
  *  
  * @param o 执行对象 
  * @param fieldName 属性 
  * @param value 值 
  * @param fnType "get" or "set"
  */  
  public static void invokeFn(Object o, String fieldName, Object value,String fnType) {  
      Method method = getFnMethod(o.getClass(), fieldName,fnType);  
      try {  
      //指定对象的成员类型,可以切换Object为对应类型进行Set和Get
          method.invoke(o, new Object[] { value });  
      } catch (Exception e) {  
          e.printStackTrace();  
      }  
  }