BeanUtils代码改写成BeanCopier时出现的问题

来源:互联网 发布:个人日志提醒软件 编辑:程序博客网 时间:2024/05/17 07:05

错误案例

短信平台大量的使用到了org.apache.commons.beanutils.BeanUtils的copyProperties(Object dest, Object orig)方法。而这个方法存在性能问题,在性能优化小组的建议下改使用net.sf.cglib.beans.BeanCopier的copy(Object from, Object to, Converter converter)方法将其替换。例如:

原来代码:BeanUtils.copyProperties(cpInfoDO, cpInfo);                改写后的代码:BeanCopier beanCopier = BeanCopier.create(CPInfo.class, CPInfoDO.class, false);beanCopier.copy(cpInfo, cpInfoDO, null);

自测时发现修改短信平台内容提供商的form提交总是失败,webx.log抛出的异常显示,插入数据库时,DO对象中非空字段出现了空值。

错误分析

比较了copy属性的两个对象,CPInfo中的status属性是StatusEnum类型(该类型继承了com.alibaba.common.lang.enumeration.IntegerEnum),而CPInfoDO中的status属性是String类型的。

1.使用BeanUtils进行属性拷贝时,CPInfo中的status属性值可以拷贝到CPInfo中的status属性值中,虽然两者类型不相同,而BeanCopier却不行。

结合本例分析BeanUtils的拷贝属性是通过反射实现的,关键代码实现和分析如下:

BeanUtilsBean:392  descriptor =getPropertyUtils().getPropertyDescriptor(target, name);   // 获得属性描述,这里target为CPInfoDO的一个实例,name为status     ... 400  type = descriptor.getPropertyType();                                  // 获得属性的类型,CPInfoDO的类型是String     ...441  Converter converter = getConvertUtils().lookup(type);                 // 查找String类型的转换器,StringConverter 442  if (converter != null) {443     log.trace("        USING CONVERTER " + converter);444     value = converter.convert(type, value);                            // 进行转换,value是CPInfo实例的status属性445  }StringConverter:       public Object convert(Class type, Object value) {      if (value == null) {         return ((String) null);      } else {         return (value.toString());                                        // 虽然StatusEnum不是String类型,但是它有toString方法,所以不同类型的属性还是可以copy成功      }   }

2.BeanCopier会通过字节码生成动态代理对象,代理对象拷贝属性时会检查类型,类型不同认为是不同的属性,就不进行赋值拷贝了。


本例中CPInfo的status是StatusEnum类型的,CPInfoDO是String类型,两者类型不一致,不进行属性拷贝赋值。数据库插入时,CPInfoDO的status为null,于是就出现了非空字段插入为空的异常了。

正确用法

BeanCopier beanCopier = BeanCopier.create(CPInfo.class, CPInfoDO.class, false);beanCopier.copy(cpInfo, cpInfoDO, null);// beanCopier复制属性时,属性类型不同不能复制,需要自己set下cpInfoDO.setStatus(cpInfo.getStatus().getName());

测试关注点

做好单元测试,保证方法输入输出一致

自动化回归测试,并关注后台日志


原创粉丝点击