分享

初用apache.commons.beanutils.BeanUtils [和讯博客]

 深呼吸 2005-12-27
初用apache.commons.beanutils.BeanUtils
 转贴   fy198392 2005-11-25 11:23:59 查看评论   
  
 
引言

该class提供了一系列的静态方法操作业已存在的符合JavaBean规范定义的Java Class.这里强调的JavaBean规范,简单来说就是一个Java Class通过一系列getter和setter的方法向外界展示其内在的成员变量(属性).通过BeanUtils的静态方法,我们可以:

  • 复制一个JavaBean的实例--BeanUtils.cloneBean();
  • 在一个JavaBean的两个实例之间复制属性--BeanUtils.copyProperties(),BeanUtils.copyProperty();
  • 为一个JavaBean的实例设置成员变量(属性)值--BeanUtils.populate(),BeanUtils.setProperty();
  • 从一个一个JavaBean的实例中读取成员变量(属性)的值--BeanUtils.getArrayProperty(),BeanUtils.getIndexedProperty(),BeanUtils.getMappedProperty(),BeanUtils.getNestedProperty(),BeanUtils.getSimpleProperty(),BeanUtils.getProperty(),BeanUtils.describe();

总的来看BeanUtils类提供了两大类的功能:读,写成员变量.

准备工作

下面逐一分析使用方法.首先我们建立两个JavaBean,名位SampleObject和SampleObjectA,具体如下:

package beanutil;

import java.util.HashMap;
import java.util.Map;

/**
 * @author samepoint
 *
 * SampleObject contains some types of member varaibles:String,int,Array,Map,Object(self defined),just for test usaged of apache.commons.beanutils.BeanUtils
 */
public class SampleObject {
 String name = null;
 String display = null;
 int num = -1;
 char[] words = {‘a‘,‘b‘,‘c‘,‘d‘};
 boolean tag = false;
 Map map = new HashMap();
 SampleObjectA sample = null;

 /**
  * default constructor. initialized members of map and sample.
  */
 public SampleObject() {
  this.map.put("home","localhost");
  this.map.put("port","80");
 }

//the following is getters and setters
 /**
  * @return Returns the display.
  */
 public String getDisplay() {
  return display;
 }
 /**
  * @param display The display to set.
  */
 public void setDisplay(String display) {
  this.display = display;
 }
 /**
  * @return Returns the name.
  */
 public String getName() {
  return name;
 }
 /**
  * @param name The name to set.
  */
 public void setName(String name) {
  this.name = name;
 }
 /**
  * @return Returns the num.
  */
 public int getNum() {
  return num;
 }
 /**
  * @param num The num to set.
  */
 public void setNum(int num) {
  this.num = num;
 }
 /**
  * @return Returns the words.
  */
 public char[] getWords() {
  return words;
 }
 /**
  * @param words The words to set.
  */
 public void setWords(char[] words) {
  this.words = words;
 }
 /**
  * @return Returns the tag.
  */
 public boolean isTag() {
  return tag;
 }
 /**
  * @param tag The tag to set.
  */
 public void setTag(boolean tag) {
  this.tag = tag;
 }
 /**
  * @return Returns the map.
  */
 public Map getMap() {
  return map;
 }
 /**
  * @param map The map to set.
  */
 public void setMap(Map map) {
  this.map = map;
 }
 /**
  * @return Returns the sample.
  */
 public SampleObject getSample() {
  return sample;
 }
 /**
  * @param sample The sample to set.
  */
 public void setSample(SampleObject sample) {
  this.sample = sample;
 }
}

package beanutil;

/**
 * @author samepoint
 *
 * Used to copy properties from SampleOjbect.
 * Used to nested property.
 */
public class SampleObjectA {
 String name = null;
 String display = null;
 Double num = null;

 /**
  * @return Returns the num.
  */
 public Double getNum() {
  return num;
 }
 /**
  * @param num The num to set.
  */
 public void setNum(Double num) {
  this.num = num;
 }
 /**
  * @return Returns the display.
  */
 public String getDisplay() {
  return display;
 }
 /**
  * @param display The display to set.
  */
 public void setDisplay(String display) {
  this.display = display;
 }
 /**
  * @return Returns the name.
  */
 public String getName() {
  return name;
 }
 /**
  * @param name The name to set.
  */
 public void setName(String name) {
  this.name = name;
 }
}

所有测试使用的bean,如果未有说明,均使用SampleObject.

所有测试使用的bean,如果未有说明,均使用SampleObject.
BeanUtils.cloneBean(java.lang.object bean)

为bean创建一个clone对象,方法返回类型为Object.注意bean即使没有实现java.lang.Cloneable接口,此方法依然有效.此方法的实现机制建立在bean提供的一系列的getters和setters的基础之上.此方法的正常使用代码非常简单,故略掉.


下面讨论下如果bean没有提供getters和setters,会出现什么情况,很明显如果将其中的一对getter和setter注释掉,如getDisplay()和setDisplay(),那么结果是根本不会针对display这个成员变量进行复制;另外,如果将setDisplay()的访问限定符号设置为private的话,结果也是一样的,成员变量-display在clone的过程中不会被复制.注意上面讨论的两种情况,在运行时不会抛出任何的exception.对于不抛出exception的问题,我也感到非常迷惑,因为此方法的javadoc上明明指出当不能访问bean上的accessor或不存在accessor时,应该抛出java.lang.IllegalAccessException或java.lang.NotSuchMethodException.为了再次确认,我将SampleObject中的所有getter和setter都注释掉了,结果依然一样,看来要看下源码了.

BeanUtils.copyProperties(java.lang.Object dest, java.lang.Object orig)

一个bean class有两个实例:orig和dest,将orig中的成员变量的值复制给dest,即将已经存在的dest变为orig的副本.与BeanUtils.cloneBean(java.lang.object bean)的区别就在于是不是需要创建新的实例了.同样正常使用代码非常简单,这里也略掉.
如果bean class中没有提供或是不完全提供getters和setters,结果如同在BeanUtils.cloneBean(java.lang.object bean)部分中的讨论结果一样.
另外,我曾经这样想,如果有两个bean class,他们之间没有任何关系,只是在成员变量的命名上有重叠(以SampleObject为例,如果我们有另外的bean class--AnotherSampleObject,也包含了成员变量display,name和num),他们之间是否可以利用BeanUtils.copyProperties(java.lang.Object dest, java.lang.Object orig)进行复制呢?(这个想法来自于<struts in action>中formBean章节中关于formBean与valueObject的讨论)答案是可以的,该方法会复制名称完全一样的成员变量,即使成员变量的类型不同也会自动进行转换的(我在AnotherSampleObject中将num的类型定义为Double,而SampleObject中的num为int),感觉真的是很神奇.回头再去看看javadoc,发现这个方法原本就是如此设计的,原文如下:
Copy property values from the origin bean to the destination bean for all cases where the property names are the same.
其中for all cases where the property names are the same正是很好的明证,以后可以放心大胆的使用了.
ps:又对javadoc的重要性进行重新认识,同时认识到自己的英文是那么的烂.

BeanUtils.copyProperty(java.lang.Object bean,java.lang.String name,java.lang.Object value)

这个方法简单的说就是将bean中的成员变量name赋值为value.使用方法如下:
SampleObject sample = new SampleObject();
 BeanUtils.copyProperty(sample,"num",new Integer(10));

如果成员变量为数组,如何为数据内的成员赋值呢?apache的java doc上说的很明白,就是要提供一个包含索引参数的setter,所以要将以下代码加到SampleObject的源代码中.
/**
  * set word with against
  * @param index
  * @param word
  */
 public void setWords(int index,char word){
  this.words[index] = word;
 }

如果我们要为SampleObject中的words[2]赋值为S,那么代码如下:
BeanUtils.copyProperty(a,"words[2]","S");

如果成员变量为Map,如何为Map内指定key赋值呢?同上面讲的数组的方式一样,就是要提供一个包含key参数的setter,在SampleObject中添加如下代码:
/**
  * set map with key
  * @param key
  * @param value
  */
 public void setMap(Object key,Object value){
  this.map.put(key,value);
 }

如果我们要将SampleObject.map中home对应值改为remote,那么代码如下:
BeanUtils.copyProperty(a,"map(home)","remote");

最后说下如何为嵌套属性的赋值,(所谓嵌套属性就是beanA中一个成员变量是另外一个beanB,那么beanB中的属性就叫做beanA的嵌套属性了.),用法如下:
BeanUtils.copyProperty(a,"sample.display","second one");

BeanUtils.setProperty(java.lang.Object bean,java.lang.String name,java.lang.Object value)

这个方法让我郁闷了一会,因为它提供的功能与上面说的BeanUtils.copyProperty(java.lang.Object bean,java.lang.String name,java.lang.Object value)完全一致,apache的hero们没理由为同一功能提供两种展示方法啊,后来我看了apache.commons.beanutils.BeanUtilsBean中的javadoc,才明白了一点点.如果我们只是为bean的属性赋值的话,使用copyProperty()就可以了;而setProperty()方法是实现BeanUtils.populate()(后面会说到)机制的基础,也就是说如果我们需要自定义实现populate()方法,那么我们可以override setProperty()方法.
所以,做为一般的日常使用,setProperty()方法是不推荐使用的.

BeanUtils.populate(java.lang.Object bean, java.util.Map properties)

使用一个map为bean赋值,该map中的key的名称与bean中的成员变量名称相对应.注意:只有在key和成员变量名称完全对应的时候,populate机制才发生作用;但是在数量上没有任何要求,如map中的key如果是成员变量名称的子集,那么成员变量中有的而map中不包含的项将会保留默认值;同样,如果成员变量是map中key的子集,那么多余的key不会对populate的结果产生任何影响.恩,结果就是populate只针对map中key名称集合与bean中成员变量名称集合的交集产生作用.(很饶口啊)
正常用法很简单,这里略掉.
同样,这个方法也支持对数组中单个元素,map中单个元素和嵌套属性的赋值,具体做法和copyProperty()方法类似,具体如下:
 values.put("words[1]","U");
  values.put("map(home)","remote");
  values.put("sample.display",new Double(5.0));

注意:apache的javadoc中,明确指明这个方法是为解析http请求参数特别定义和使用的,在正常的使用中不推荐使用.他们推荐使用BeanUtils.copyProperties()方法.(struts中的FormBean应该是用这个方法装配的)

BeanUtils.getArrayProperty(java.lang.Object bean,java.lang.String name)

获取bean中数组成员变量(属性)的值.
没什么好说的,用法很简单,略.
还是要说一句,如果我们指定的name不是数组类型的成员变量,结果会如何?会不会抛出类型错误的exception呢?回答是不会,仍然会返回一个String的数组,数组的第一项就是name对应的值(如果不是String类型的话,JVM会自动的调用toString()方法的).

BeanUtils.getIndexedProperty(java.lang.Object bean,java.lang.String name)
BeanUtils.getIndexedProperty(java.lang.Object bean,java.lang.String name,int index)

这两个方法都是获取数组成员变量(属性)中的单一元素值的方法.比如,我想得到SampleObject中words[1]的值,用法如下:
BeanUtils.getIndexedProperty(sampleOjbectInstance,"words[1]");
 BeanUtils.getIndexedProperty(sampleOjbectInstance,"words",1);

BeanUtils.getMappedProperty(java.lang.Object bean,java.lang.String name)
BeanUtils.getMappedProperty(java.lang.Object bean,java.lang.String name,java.lang.String key)

这两个方法是获取map成员变量中单一元素值的方法,用法与getIndexedProperty()方法相似,如我想得到SampleObject中map中home对应的值,用法如下:
BeanUtils.getMappedProperty(sampleOjbectInstance,map(home));
 BeanUtils.getMappedProperty(sampleOjbectInstance,map,"home");

BeanUtils.getNestedProperty(java.lang.Object bean,java.lang.String name)

获取嵌套属性值的方法,如我想得到SampleOjbect中成员变量sample中的display的值,用法如下:
BeanUtils.getNestedProperty(sampleOjbectInstance,"sample.display");

BeanUtils.getSimpleProperty(java.lang.Object bean, java.lang.String name)
BeanUtils.getProperty(java.lang.Object bean, java.lang.String name)

获取属性值的方法.api已经很清楚了,我唯一的问题是这个simple是什么意思.javadoc只是说了getProperty()方法中的name参数可以为普通属性名称,数组属性名称或嵌套属性名称的一种,而getSimpleProperty()方法中的name参数应该为普通属性名称了.我的想法是通过对方法签名的不同,让developers可以显示区别对待普通属性,数组属性,map属性和嵌套属性.
ps:具体有何区别,看来要仔细看看源代码了.

BeanUtils.describe(java.lang.Object bean)

将一个bean以map的形式展示.(这个方法和populate()是我梦想中的双手剑)
但是使用这个方法得到的结果有点令我失望,以SampleObject为例,代码片段如下:
 SampleObject a = new SampleObject();
  a.setDisplay("first one");
  a.setName("A");
  a.setNum(5);
  a.setWords("goto".toCharArray());
  SampleObjectA b = new SampleObjectA();
  b.setDisplay("nested property");
  b.setNum(new Double(2.0));
  b.setName("sampleA");
  a.setSample(b);
  try {
   Map descMap = BeanUtils.describe(a);
   System.out.println(descMap);
  }
  ......

运行结果如下:
{num=5, display=first one, class=class beanutil.SampleObject, words=g, tag=false, sample=beanutil.SampleObjectA@be2358, map={port=80, home=localhost}, name=A}

  • 首先可以看出,除了输出SampleObject中定义的key-value外,还会包含class=class beanutil.SampleObject这一项,我想这是为了通过获得的map我们可以知道原来的bean的具体类型;
  • 其次,作为数组成员变量(属性)的words,在map中只包含了首个元素,而map类型的成员变量的输出结果到是非常令人满意.为什么明明长度为4的words数组现在输出只有一个字符呢,我又进行了debug,并监控了words变量,发现在返回的descMap中,words对应的值的类型为String,长度为1.
    ps:不知道是不是我使用错误,真不知道为什么会这样.
  • 最后,嵌套属性不会逐一进行输出的,除非你override了toString()方法.
与apache.commons.beanutils.BeanUtilsBean的关系

apache.commons.beanutils.BeanUtils中每个方法是通过apache.commons.beanutils.BeanUtilsBean实现的,apache.commons.beanutils.BeanUtils中静态方法功能是默认方法,也就是最基本和最普通的,如果需要更复杂的功能实现的话,则需要使用apache.commons.beanutils.BeanUtilsBean中的方法.apache.commons.beanutils.BeanUtilsBean可以在不同的缓冲区内存在不同的实例,从而可以提供不同的服务,主要是converter的不同.通过这个机制可以为不同的用户提供本地化的支持(我想这个在internet application上经常要用到吧).我想这也是为什么apache.commons.beanutils.BeanUtilsBean不是interface而是class的原因.

 

[发起辩论]
[收藏到我的网摘]
[推荐] | [评论] | [投诉] | [打印]
本文章被推荐到了0 个圈子
点击数: 4
评论数: 1

本文章引用通告地址(TrackBack Ping URL)为:
http://blog.hexun.com/fy198392/trackback.aspx?articleid=1544118
本文章尚未被引用。

进入该博客主页
 


fy198392
[发送私信]
[加为好友]
Re: 初用apache.commons.beanutils.BeanUtils [2005-11-25 11:39:01]  
BeanUtils简读本

一、简介:

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=;HibernateDAO.save(teacher);而使用BeanUtils后,代码就大大改观了,如下所示://得到TeacherFormTeacherForm teacherForm=(TeacherForm)form;//构造Teacher对象Teacher teacher=new Teacher();//赋值BeanUtils.copyProperties(teacher,teacherForm);//持久化Teacher对象到数据库HibernateDAO=;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方法),以及通过串行化将其返回到远程的客户机的时间总和。所以要小心使用这种威力!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多