java bean拷貝操作又一個(gè)非常好用的工具類 BeanUitls :
spring (org.springframework.beans.BeanUtils)和apache commons-beanutils(org.apache.commons.beanutils.BeanUtils)中分別存在一個(gè)BeanUtils,提供了對(duì)。
特別注意 這兩個(gè)類在不同的包下面,而這兩個(gè)類的copyProperties()方法里面?zhèn)鬟f的參數(shù)賦值是相反的。
例如:
a,b為對(duì)象
BeanUtils.copyProperties(a, b);
BeanUtils是org.springframework.beans.BeanUtils,
a拷貝到b
BeanUtils是org.apache.commons.beanutils.BeanUtils,
b拷貝到a
之前在寫程序時(shí),用到了兩個(gè)不同類型但屬性基本相同的對(duì)象的拷貝,由于類型不同源bean里屬性(Integer 向 int 拷貝)其值為null,這時(shí)會(huì)拋異常。
/**
* 測(cè)試 spring 中的 bean 拷貝
* @throws Exception
*/
@org.junit.Test
public void testBeanCopy() throws Exception {
PersonEntity pe = new PersonEntity();
pe.setAge(1);
//pe.setId(1234); // id 為 Integer 此時(shí)為null
pe.setName("kevin");
Person person = new Person(); // Person 中的id為int類型
BeanUtils.copyProperties(pe, person);
System.out.println(person);
}
一個(gè)看似簡(jiǎn)單的問題困擾了在當(dāng)時(shí),于是抽空看了一下spring和apache commons-beanutils包中BeanUtils.copyProperties的實(shí)現(xiàn)。
spring中實(shí)現(xiàn)的方式很簡(jiǎn)單,就是對(duì)兩個(gè)對(duì)象中相同名字的屬性進(jìn)行簡(jiǎn)單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則施加了很多的檢驗(yàn),包括類型的轉(zhuǎn)換,甚至于還會(huì)檢驗(yàn)對(duì)象所屬的類的可訪問性。
源碼
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的。除了支持基本類型以及基本類型的數(shù)組之外,還支持java.sql.Date, java.sql.Time, java.sql.TimeStamp, java.io.File, javaio.URL這些類的對(duì)象,其余一概不支持。不過你可以自定義你的類的Converter。然后注冊(cè)進(jìn)去。所以在使用時(shí)
感覺commons-beanutils包中的這個(gè)BeanUtils類的copyProperties方法,太過復(fù)雜,約束太多,而且使用不便,雖然可擴(kuò)展性好了,但是易用性不高。
總結(jié):
關(guān)于bean復(fù)制,如果屬性較少,建議直接寫個(gè)方法完成get/set即可。如果屬性較多,可以自己采用反射實(shí)現(xiàn)一個(gè)滿足自己需要的工具類,或者使用spring的那個(gè)beanutils類,不建議使用commons-beanutils包中的那個(gè)BeanUtils類,剛看了下,這個(gè)類對(duì)于內(nèi)部靜態(tài)類的對(duì)象復(fù)制也會(huì)出現(xiàn)問題,檢驗(yàn)太復(fù)雜了,常會(huì)出現(xiàn)一些詭異的問題。畢竟我們bean復(fù)制一般就是簡(jiǎn)單的屬性copy而已。
而且,由于這些BeanUtils類都是采用反射機(jī)制實(shí)現(xiàn)的,對(duì)程序的效率也會(huì)有影響。因此,慎用BeanUtils.copyProperties?。?!或者使用clone看效果如何!