copyProperties的用法

来源:互联网 发布:北京四达时代 知乎 编辑:程序博客网 时间:2024/05/16 06:40

BeanUtils和PropertyUtils这两个类都有copyProperties方法,其中BeanUtils 类继承了 PropertyUtils 类,它们两个区别如下

 

网文网址:http://www.cnblogs.com/fayf/articles/1272982.html

一、简介:
BeanUtils提供对Java反射和自省API的包装。其主要目的是利用反射机制对JavaBean的属性进行处理。我们知道,一个JavaBean通常包含了大量的属性,很多情况下,对JavaBean的处理导致大量get/set代码堆积,增加了代码长度和阅读代码的难度。

二、用法:
BeanUtils是这个包里比较常用的一个工具类,这里只介绍它的copyProperties()方法。该方法定义如下:

 

public static void copyProperties(java.lang.Object dest,java.lang.Object orig)throws java.lang.IllegalAccessException,java.lang.reflect.InvocationTargetException

 

如果你有两个具有很多相同属性的JavaBean,一个很常见的情况就是Struts里的PO对象(持久对象)和对应的ActionForm,例如 Teacher和TeacherForm。我们一般会在Action里从ActionForm构造一个PO对象,传统的方式是使用类似下面的语句对属性逐个赋值:

//得到TeacherFormTeacherForm teacherForm=(TeacherForm)form;//构造Teacher对象Teacher teacher=new Teacher();//赋值teacher.setName(teacherForm.getName());teacher.setAge(teacherForm.getAge());teacher.setGender(teacherForm.getGender());teacher.setMajor(teacherForm.getMajor());teacher.setDepartment(teacherForm.getDepartment());//持久化Teacher对象到数据库HibernateDAO.save(teacher);

 

而使用BeanUtils后,代码就大大改观了,如下所示://得到TeacherFormTeacherForm teacherForm=(TeacherForm)form;//构造Teacher对象Teacher teacher=new Teacher();//赋值BeanUtils.copyProperties(teacher,teacherForm);//持久化Teacher对象到数据库HibernateDAO.save(teacher);


 

如果Teacher和TeacherForm间存在名称不相同的属性,则BeanUtils不对这些属性进行处理,需要程序员手动处理。例如 Teacher包含modifyDate(该属性记录最后修改日期,不需要用户在界面中输入)属性而TeacherForm无此属性,那么在上面代码的 copyProperties()后还要加上一句:

teacher.setModifyDate(new Date());

怎么样,很方便吧!除BeanUtils外还有一个名为PropertyUtils的工具类,它也提供copyProperties()方法,作用与 BeanUtils的同名方法十分相似,主要的区别在于后者提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,而前者不支持这个功能,但是速度会更快一些。BeanUtils支持的转换类型如下:

* java.lang.BigDecimal* java.lang.BigInteger* boolean and java.lang.Boolean* byte and java.lang.Byte* char and java.lang.Character* java.lang.Class* double and java.lang.Double* float and java.lang.Float* int and java.lang.Integer* long and java.lang.Long* short and java.lang.Short* java.lang.String* java.sql.Date* java.sql.Time* java.sql.Timestamp 

 

这里要注意一点,java.util.Date是不被支持的,而它的子类java.sql.Date是被支持的。因此如果对象包含时间类型的属性,且希望被转换的时候,一定要使用java.sql.Date类型。否则在转换时会提示argument mistype异常。

三、优缺点:

Apache Jakarta Commons项目非常有用。我曾在许多不同的项目上或直接或间接地使用各种流行的commons组件。其中的一个强大的组件就是BeanUtils。我 将说明如何使用BeanUtils将local实体bean转换为对应的value 对象:

BeanUtils.copyProperties(aValue, aLocal)

 

上面的代码从aLocal对象复制属性到aValue对象。它相当简单!它不管local(或对应的value)对象有多少个属性,只管进行复制。我们假设 local对象有100个属性。上面的代码使我们可以无需键入至少100行的冗长、容易出错和反复的get和set方法调用。这太棒了!太强大了!太有用 了!

现在,还有一个坏消息:使用BeanUtils的成本惊人地昂贵!我做了一个简单的测试,BeanUtils所花费的时间要超过取数 据、将其复制到对应的 value对象(通过手动调用get和set方法),以及通过串行化将其返回到远程的客户机的时间总和。所以要小心使用这种威力!

 
 
 
执行这个方法牵扯到类型转换时要注意的问题:
 

有关BeanUtils的copyProperties

BeanUtils是org.apache.commons的一个很重要的包,主要提供一些对Bean的操作。这个类我很早就接触了,大概是再2年多以前。当时还是大家在疯狂讨论J2EE每个层次是否都应该拥有各自的Object,并是否需要在这些Object之间进行互相转化的年代。而当时,有很多激进派都坚决主张引入DTO或者VO(事实上这是一种反模式),他们的观点很简单,每个层次含义不同,对应运行的Object也应该不同,而他们之间的转化,由BeanUtils来做是相当简单的,一句话就可以了。
当时对这种转化的言论我没有发表任何议论,因为领悟不深。但是对于BeanUtils这个有用的类,倒是一直在使用,因为他比较简单。一个静态方法,使用反射,马上就可以复制一个Bean出来。当时脑子里面一闪而过的印象只是,这样的copy肯定比我手工一个一个copy的效率低,因为他用了反射。不过反射能差多少效率呢?Spring不是通篇用了反射嘛?因而更加细一步的内容和源码也没有自己探究。
直到两年后的今天,没想到这个BeanUtils的copyProperties方法却给我带来了极大的麻烦。请看下面的testCase:

   

/**    *    *@throwsException    */    publicvoid testBeanUtilsCopyProperties()throws Exception {       Admin admin = new Admin();       User user = new User();       user.setId(new Long(1));       user.setName("downpour");       user.setRate(new Double(1));       logger.info("before copy:");       logger.info("admin id:" + admin.getId());       logger.info("admin name:" + admin.getName());       logger.info("admin password:" + (admin.getPassword() == null));       logger.info("admin birthday:" + admin.getBirthday());       logger.info("admin mark:" + admin.getMark());       logger.info("admin sex:" + admin.getSex());       BeanUtils.copyProperties(admin, user);       logger.info("after copy:");       logger.info("admin id:" + admin.getId());       logger.info("admin name:" + admin.getName());       logger.info("admin password:" + (admin.getPassword() == null));       logger.info("admin birthday:" + admin.getBirthday());       logger.info("admin mark:" + admin.getMark());       logger.info("admin sex:" + admin.getSex());    }


 

其中,admin和user分别拥有共有的字段:id(java.lang.Long),name(java.lang.String),password(java.lang.String),birthday(java.util.Date),sex(java.lang.Integer)。然后我运行上面的TestCase,在User中,我特地没有给sex这个字段赋值,让他的值为null。运行的结果却让我郁闷:

09:20:24,221 INFO BeanUtilsTest:49 - before copy:09:20:24,221 INFO BeanUtilsTest:50 - admin id:null09:20:24,221 INFO BeanUtilsTest:51 - admin name:null09:20:24,221 INFO BeanUtilsTest:52 - admin password:true09:20:24,231 INFO BeanUtilsTest:53 - admin birthday:null09:20:24,231 INFO BeanUtilsTest:54 - admin mark:null09:20:24,231 INFO BeanUtilsTest:55 - admin sex:null09:20:24,291 INFO BeanUtilsTest:59 - after copy:09:20:24,291 INFO BeanUtilsTest:60 - admin id:109:20:24,301 INFO BeanUtilsTest:61 - admin name:downpour09:20:24,301 INFO BeanUtilsTest:62 - admin password:true09:20:24,301 INFO BeanUtilsTest:63 - admin birthday:null09:20:24,301 INFO BeanUtilsTest:64 - admin mark:null09:20:24,301 INFO BeanUtilsTest:65 - admin sex:0


 

注意最后一行,明明没有给User赋值,做了一次copy,却带入了一个0到新的Admin中去。这不是强买强卖嘛?而且我特地用了java.lang.Integer的外覆类,那应该他是null,你就copy一个null过去呗,没事情你给我初始化一下做啥?汗~~~

后来看了源码,才发现,原来是类型转化惹的祸。由于在BeanUtils中,隐含了一个对于不同类型之间进行转化的步骤。也就是说,如果你的Source Bean中某个字段原本是String,而Dest Bean中的对应字段是Integer,它在可以进行转化时可以帮你做自动的转化。在这个转化中,会在中间变量中都先new一个default值set进去。昏过去。。。

看来这个方法在做copy时实在是不安全,尤其是对于那些null值本身有含义的情况下,只能舍弃这个方法。幸好,apache还有点人性,提供了一个叫做PropertyUtils的copyProperties方法。这个方法不会去做相应的类型转化。可以放心使用,不过如果你的Bean的类型不匹配,那么它会报错。