BeanUtils 工具 copyProperties 拷貝對象


java bean拷貝操作又一個非常好用的工具類 BeanUitls :
spring (org.springframework.beans.BeanUtils)和apache commons-beanutils(org.apache.commons.beanutils.BeanUtils)中分別存在一個BeanUtils,提供了對。
特別注意 這兩個類在不同的包下面,而這兩個類的copyProperties()方法里面傳遞的參數賦值是相反的。
例如:
a,b為對象
BeanUtils.copyProperties(a, b);

BeanUtils是org.springframework.beans.BeanUtils,
a拷貝到b
BeanUtils是org.apache.commons.beanutils.BeanUtils,
b拷貝到a

之前在寫程序時,用到了兩個不同類型但屬性基本相同的對象的拷貝,由於類型不同源bean里屬性(Integer 向 int 拷貝)其值為null,這時會拋異常。

 /**
* 測試 spring 中的 bean 拷貝
* @throws Exception
*/

@org.junit.Test
public void testBeanCopy() throws Exception {

PersonEntity pe = new PersonEntity();
pe.setAge(1);
//pe.setId(1234); // id 為 Integer 此時為null
pe.setName("kevin");

Person person = new Person(); // Person 中的id為int類型

BeanUtils.copyProperties(pe, person);

System.out.println(person);

}


一個看似簡單的問題困擾了在當時,於是抽空看了一下spring和apache commons-beanutils包中BeanUtils.copyProperties的實現。

spring中實現的方式很簡單,就是對兩個對象中相同名字的屬性進行簡單get/set,僅檢查屬性的可訪問性。

源碼

package org.springframework.beans;

private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties)
throws BeansException
{

Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");

Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}

而commons-beanutils則施加了很多的檢驗,包括類型的轉換,甚至於還會檢驗對象所屬的類的可訪問性。

源碼

package org.apache.commons.beanutils;

public void copyProperties(Object dest, Object orig)
throws IllegalAccessException, InvocationTargetException {

// Validate existence of the specified beans
if (dest == null) {
throw new IllegalArgumentException
("No destination bean specified");
}
if (orig == null) {
throw new IllegalArgumentException("No origin bean specified");
}
if (log.isDebugEnabled()) {
log.debug("BeanUtils.copyProperties(" + dest + ", " +
orig + ")");
}

// Copy the properties, converting as necessary
if (orig instanceof DynaBean) {
DynaProperty origDescriptors[] =
((DynaBean) orig).getDynaClass().getDynaProperties();
for (int i = 0; i < origDescriptors.length; i++) {
String name = origDescriptors[i].getName();
if (getPropertyUtils().isWriteable(dest, name)) {
Object value = ((DynaBean) orig).get(name);
copyProperty(dest, name, value);
}
}
} else if (orig instanceof Map) {
Iterator names = ((Map) orig).keySet().iterator();
while (names.hasNext()) {
String name = (String) names.next();
if (getPropertyUtils().isWriteable(dest, name)) {
Object value = ((Map) orig).get(name);
copyProperty(dest, name, value);
}
}
} else /* if (orig is a standard JavaBean) */ {
PropertyDescriptor origDescriptors[] =
getPropertyUtils().getPropertyDescriptors(orig);
for (int i = 0; i < origDescriptors.length; i++) {
String name = origDescriptors[i].getName();
if ("class".equals(name)) {
continue; // No point in trying to set an object's class
}
if (getPropertyUtils().isReadable(orig, name) &&
getPropertyUtils().isWriteable(dest, name)) {
try {
Object value =
getPropertyUtils().getSimpleProperty(orig, name);
copyProperty(dest, name, value);
} catch (NoSuchMethodException e) {
; // Should not happen
}
}
}
}

}

而且,commons-beanutils中的裝換是不支持java.util.Date的。除了支持基本類型以及基本類型的數組之外,還支持java.sql.Date, java.sql.Time, java.sql.TimeStamp, java.io.File, javaio.URL這些類的對象,其余一概不支持。不過你可以自定義你的類的Converter。然后注冊進去。所以在使用時

感覺commons-beanutils包中的這個BeanUtils類的copyProperties方法,太過復雜,約束太多,而且使用不便,雖然可擴展性好了,但是易用性不高。

總結:

關於bean復制,如果屬性較少,建議直接寫個方法完成get/set即可。如果屬性較多,可以自己采用反射實現一個滿足自己需要的工具類,或者使用spring的那個beanutils類,不建議使用commons-beanutils包中的那個BeanUtils類,剛看了下,這個類對於內部靜態類的對象復制也會出現問題,檢驗太復雜了,常會出現一些詭異的問題。畢竟我們bean復制一般就是簡單的屬性copy而已。

而且,由於這些BeanUtils類都是采用反射機制實現的,對程序的效率也會有影響。因此,慎用BeanUtils.copyProperties!!!或者使用clone看效果如何!


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com