apache.commons.beanutils.BeanUtils

本文详细介绍BeanUtils类,它提供了一系列静态方法用于操作符合JavaBean规范的类。文章深入探讨了复制、赋值、读取JavaBean属性的方法,并解析其实现原理。
该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的原因.

总结
BeanUtils是利用java的反射和自醒机制来读写javabean的属性的.

http://dev.youkuaiyun.com/htmls/72/72730.html
<think> 我们正在解决用户关于`org.apache.commons.beanutils.BeanUtils`依赖的问题。用户提到自己下载了jar包,但运行时出现了`java.lang.NoClassDefFoundError`。这通常意味着编译时找到了类,但运行时类路径(classpath)中缺少相应的jar包。 根据用户提供的引用[1]和[2],问题可能涉及到两个不同的Apache Commons组件: - `commons-beanutils`: 包含`BeanUtils`类 - `commons-lang`: 包含`NestableRuntimeException`类(在引用[2]中缺失) 用户遇到的错误是: 1. 编译通过,但运行时报`java.lang.NoClassDefFoundError: org/apache/commons/beanutils/BeanUtils` -> 说明运行时缺少`commons-beanutils`的jar。 2. 另外引用[2]提到`org.apache.commons.lang.exception.NestableRuntimeException`找不到,这属于`commons-lang`。 因此,我们需要确保两个依赖都正确引入。 ### 解决方案 #### 1. Maven项目 如果是Maven项目,在`pom.xml`中添加以下依赖: ```xml <dependencies> <!-- BeanUtils 核心依赖 --> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version> </dependency> <!-- 可选的,如果还需要commons-lang --> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> </dependencies> ``` #### 2. 手动添加JAR 如果手动管理JAR,需要下载以下两个JAR包并添加到项目的类路径中: - [commons-beanutils-1.9.4.jar](https://commons.apache.org/proper/commons-beanutils/download_beanutils.cgi) - [commons-lang-2.6.jar](https://commons.apache.org/proper/commons-lang/download_lang.cgi) 注意:`commons-beanutils`还依赖`commons-logging`,所以可能还需要下载[commons-logging](https://commons.apache.org/proper/commons-logging/download_logging.cgi)。 #### 3. 解决类路径问题 确保下载的JAR包已经正确添加到项目的类路径中。在IDE(如Eclipse或IntelliJ IDEA)中,需要将JAR包添加到项目的构建路径(Build Path)中。 ### 验证 添加依赖后,尝试运行以下测试代码: ```java import org.apache.commons.beanutils.BeanUtils; public class TestBeanUtils { public static void main(String[] args) { try { // 创建一个测试对象 Object object = new Object(); // 使用BeanUtils的简单方法,比如复制属性(这里仅测试类加载) System.out.println("BeanUtils loaded successfully!"); } catch (Exception e) { e.printStackTrace(); } } } ``` ### 常见问题 1. **版本冲突**:如果项目中其他依赖使用了不同版本的`commons-beanutils`或`commons-lang`,可能需要排除冲突的版本。 2. **依赖传递**:如果使用Maven,注意依赖传递可能会引入多个版本,可以通过`<exclusions>`排除。 ### 引用说明 在引用[2]中提到的`ClassNotFoundException: org.apache.commons.lang.exception.NestableRuntimeException`,这个类实际上在`commons-lang`的较老版本(2.x)中。注意,在`commons-lang3`中,包名改为`org.apache.commons.lang3`,且移除了`NestableRuntimeException`。因此,如果你使用的是`commons-lang3`,那么需要确保代码中引用的包名正确。 如果项目已经升级到`commons-lang3`,但是某个依赖仍然需要`commons-lang`(老版本),则可以同时引入两个依赖,因为它们的包名不同。 ### 总结 - 确保`commons-beanutils`的JAR在运行时类路径中。 - 根据错误提示,可能还需要`commons-lang`(老版本)的JAR。 - 使用构建工具(如Maven)可以自动处理依赖关系,避免手动管理。 §§相关问题§§ 1. 如何解决Java中的NoClassDefFoundError错误? 2. Maven项目中如何排除传递依赖? 3. commons-lang和commons-lang3有什么区别? 4. 如何在IntelliJ IDEA中添加外部JAR包? </think>### `org.apache.commons.beanutils.BeanUtils`依赖信息 #### 1. **Maven依赖配置** ```xml <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version> </dependency> ``` **最新版本**:截至2023年,最新稳定版为`1.9.4`([官方仓库](https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils)) --- #### 2. **Gradle依赖配置** ```groovy implementation 'commons-beanutils:commons-beanutils:1.9.4' ``` --- ### 常见问题解决方案 #### 问题1:`NoClassDefFoundError: BeanUtils` **原因**:运行时缺少依赖或依赖冲突 **解决**: 1. 检查是否包含所有传递依赖: ```xml <!-- 必须同时引入这些依赖 --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.2</version> </dependency> ``` 2. 使用`mvn dependency:tree`检查版本冲突 --- #### 问题2:`ClassNotFoundException: NestableRuntimeException` **原因**:老版本`commons-lang`缺失(引用[2]) **解决方案**: ```xml <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> <!-- 注意不是lang3 --> </dependency> ``` --- ### 替代方案(避免依赖问题) #### 使用Spring BeanWrapper ```java import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; Map<String, Object> map = ...; Object target = new YourBean(); BeanWrapper wrapper = new BeanWrapperImpl(target); map.forEach((key, value) -> { if (wrapper.isWritableProperty(key)) { wrapper.setPropertyValue(key, value); } }); ``` **优势**:无需额外依赖(Spring项目自带) --- ### 完整使用示例 ```java import org.apache.commons.beanutils.BeanUtils; public class User { private String name; private int age; // getters/setters } public static void main(String[] args) { Map<String, Object> map = new HashMap<>(); map.put("name", "John"); map.put("age", 30); User user = new User(); BeanUtils.populate(user, map); // 自动注入属性 } ``` > **注意**: > 1. Map的key必须与对象属性名**严格匹配**(包括大小写) > 2. 基本类型需手动转换(如String→int) > 3. 推荐使用`BeanUtils.copyProperties()`避免类型问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值