JAVA对象属性复制

        编程过程中,有些时候某些不同的类有相同的字段,可能会遇到同时给他们赋值的情况。简单的时候,可以写重复的代码,重复太多的时候,就不如给一个赋值完,直接复制给另一个对象。
        上面这种情况在数据库的实体类中比较常见。

        java对象之间属性值复制在许多开源框架中也有实现,在这里介绍自实现、Spring、apache commons-beanutils三种实现方式。

一.自己实现简单的对象属性复制
CopyUtil01.java

package com.bijian.study;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;

/*
 * 复制的对象必须同时拥有setter和getter方法,只有一个的时候会报异常,都没有的时候就不复制
 */
public class CopyUtil01 {
    
    public static void Copy(Object source, Object dest) throws Exception {
        //获取属性
        BeanInfo sourceBean = Introspector.getBeanInfo(source.getClass(), java.lang.Object.class);
        PropertyDescriptor[] sourceProperty = sourceBean.getPropertyDescriptors();

        BeanInfo destBean = Introspector.getBeanInfo(dest.getClass(), java.lang.Object.class);
        PropertyDescriptor[] destProperty = destBean.getPropertyDescriptors();

        try {
            for (int i = 0; i < sourceProperty.length; i++) {

                for (int j = 0; j < destProperty.length; j++) {

                    if (sourceProperty[i].getName().equals(destProperty[j].getName())) {
                        //调用source的getter方法和dest的setter方法
                        destProperty[j].getWriteMethod().invoke(dest, sourceProperty[i].getReadMethod().invoke(source));
                        break;
                    }
                }
            }
        } catch (Exception e) {
            throw new Exception("属性复制失败:" + e.getMessage());
        }
    }
}

 

CopyUtil02.java

package com.bijian.study;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;

/*
 * 该方法接收两个参数,一个是复制的源对象——要复制的对象,一个是复制的目标对象——对象副本。
 * 当然这个方法也可以在两个不同对象间使用,这时候只要目标对象和对象具有一个或多个相同类型及名称的属性,那么就会把源对象的属性值赋给目标对象的属性
 */
public class CopyUtil02 {

	public static <T> T getBean(T TargetBean, T SourceBean) throws Exception {
		if (TargetBean == null)
			return null;
		Field[] tFields = TargetBean.getClass().getDeclaredFields();
		Field[] sFields = SourceBean.getClass().getDeclaredFields();
		try {
			for (Field field : tFields) {
				String fieldName = field.getName();
				if (fieldName.equals("serialVersionUID"))
					continue;
				if (field.getType() == Map.class)
					continue;
				if (field.getType() == Set.class)
					continue;
				if (field.getType() == List.class)
					continue;
				for (Field sField : sFields) {
					if (!sField.getName().equals(fieldName)) {
						continue;
					}
					Class type = field.getType();
					String setName = getSetMethodName(fieldName);
					Method tMethod = TargetBean.getClass().getMethod(setName, new Class[] { type });
					String getName = getGetMethodName(fieldName);
					Method sMethod = SourceBean.getClass().getMethod(getName, null);
					Object setterValue = sMethod.invoke(SourceBean, null);
					tMethod.invoke(TargetBean, new Object[] { setterValue });
				}
			}
		} catch (Exception e) {
			throw new Exception("设置参数信息发生异常", e);
		}
		return TargetBean;
	}

	private static String getGetMethodName(String fieldName) {
		fieldName = replaceFirstCharToUpper(fieldName);
		return "get" + fieldName;
	}

	private static String getSetMethodName(String fieldName) {
		fieldName = replaceFirstCharToUpper(fieldName);
		return "set" + fieldName;
	}

	private static String replaceFirstCharToUpper(String fieldName) {
		char[] chars = new char[1];
		chars[0] = fieldName.charAt(0);
		String temp = new String(chars);
		if (chars[0] >= 'a' && chars[0] <= 'z') {
			fieldName = fieldName.replaceFirst(temp, temp.toUpperCase());
		}
		return fieldName;
	}
}

 

CopyUtil03.java

package com.bijian.study;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/*
 * 采用反射,通过源对象getter 方法获取属性值,并通过目标对象的setter方法设置到目标对象中去
 */
public class CopyUtil03 {

	/**
	 * 利用反射实现对象之间属性复制
	 * @param from
	 * @param to
	 */
	public static void copyProperties(Object from, Object to) throws Exception {
		copyPropertiesExclude(from, to, null);
	}
	
	/**
	 * 复制对象属性
	 * @param from
	 * @param to
	 * @param excludsArray 排除属性列表
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static void copyPropertiesExclude(Object from, Object to, String[] excludsArray) throws Exception {
		List<String> excludesList = null;
		if(excludsArray != null && excludsArray.length > 0) {
			excludesList = Arrays.asList(excludsArray);	//构造列表对象
		}
		Method[] fromMethods = from.getClass().getDeclaredMethods();
		Method[] toMethods = to.getClass().getDeclaredMethods();
		Method fromMethod = null, toMethod = null;
		String fromMethodName = null, toMethodName = null;
		for (int i = 0; i < fromMethods.length; i++) {
			fromMethod = fromMethods[i];
			fromMethodName = fromMethod.getName();
			if (!fromMethodName.contains("get"))
				continue;
			//排除列表检测
			if(excludesList != null && excludesList.contains(fromMethodName.substring(3).toLowerCase())) {
				continue;
			}
			toMethodName = "set" + fromMethodName.substring(3);
			toMethod = findMethodByName(toMethods, toMethodName);
			if (toMethod == null)
				continue;
			Object value = fromMethod.invoke(from, new Object[0]);
			if(value == null)
				continue;
			//集合类判空处理
			if(value instanceof Collection) {
				Collection newValue = (Collection)value;
				if(newValue.size() <= 0)
					continue;
			}
			toMethod.invoke(to, new Object[] {value});
		}
	}
	
	/**
	 * 对象属性值复制,仅复制指定名称的属性值
	 * @param from
	 * @param to
	 * @param includsArray
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static void copyPropertiesInclude(Object from, Object to, String[] includsArray) throws Exception {
		List<String> includesList = null;
		if(includsArray != null && includsArray.length > 0) {
			includesList = Arrays.asList(includsArray);	//构造列表对象
		} else {
			return;
		}
		Method[] fromMethods = from.getClass().getDeclaredMethods();
		Method[] toMethods = to.getClass().getDeclaredMethods();
		Method fromMethod = null, toMethod = null;
		String fromMethodName = null, toMethodName = null;
		for (int i = 0; i < fromMethods.length; i++) {
			fromMethod = fromMethods[i];
			fromMethodName = fromMethod.getName();
			if (!fromMethodName.contains("get"))
				continue;
			//排除列表检测
			String str = fromMethodName.substring(3);
			if(!includesList.contains(str.substring(0,1).toLowerCase() + str.substring(1))) {
				continue;
			}
			toMethodName = "set" + fromMethodName.substring(3);
			toMethod = findMethodByName(toMethods, toMethodName);
			if (toMethod == null)
				continue;
			Object value = fromMethod.invoke(from, new Object[0]);
			if(value == null)
				continue;
			//集合类判空处理
			if(value instanceof Collection) {
				Collection newValue = (Collection)value;
				if(newValue.size() <= 0)
					continue;
			}
			toMethod.invoke(to, new Object[] {value});
		}
	}
	
	/**
	 * 从方法数组中获取指定名称的方法
	 * 
	 * @param methods
	 * @param name
	 * @return
	 */
	public static Method findMethodByName(Method[] methods, String name) {
		for (int j = 0; j < methods.length; j++) {
			if (methods[j].getName().equals(name))
				return methods[j];
		}
		return null;
	}
}

 

O1.java

package com.bijian.study;

public class O1 {
    private String aac001;
    private String aac002;
    private Double ddd001;
    
    public String getAac001() {
            return aac001;
    }
    public void setAac001(String aac001) {
            this.aac001 = aac001;
    }
    public String getAac002() {
            return aac002;
    }
    public void setAac002(String aac002) {
            this.aac002 = aac002;
    }
    public Double getDdd001() {
            return ddd001;
    }
    public void setDdd001(Double ddd001) {
            this.ddd001 = ddd001;
    }
}

 

O2.java

package com.bijian.study;

public class O2 {
    private String aac001;
    private String aac002;
    private Double ddd001;
    private String aac003;
    private String aac004;
    public String getAac002() {
            return aac002;
    }
    public void setAac002(String aac002) {
            this.aac002 = aac002;
    }
    public String getAac001() {
            return aac001;
    }
    public void setAac001(String aac001) {
            this.aac001 = aac001;
    }
    public String getAac003() {
            return aac003;
    }
    public void setAac003(String aac003) {
            this.aac003 = aac003;
    }
    public String getAac004() {
            return aac004;
    }
    public void setAac004(String aac004) {
            this.aac004 = aac004;
    }
    public Double getDdd001() {
            return ddd001;
    }
    public void setDdd001(Double ddd001) {
            this.ddd001 = ddd001;
    }
}

 

CopyJunitTest.java

package com.bijian.study;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class CopyJunitTest {
	O1 o1 = null;
	O2 o2 = null;

	@Before
	public void setUp() {
		this.o1 = new O1();
		this.o2 = new O2();

		this.o1.setAac001("00007");
		this.o1.setAac002("国产零零零零七");
		this.o1.setDdd001(Double.valueOf(3.141592653589793D));
	}

	@Test
	public void test_copyUtil01() {
		try {
			CopyUtil01.Copy(this.o1, this.o2);
			System.out.println("CopyUtil01单元测试");

			System.out.println("-------------源对象o1----------------");
			showObjectPropertyValue(this.o1);
			System.out.println("-------------目标对象o2----------------");
			showObjectPropertyValue(this.o2);
			System.out.println();
		} catch (Exception e) {
			Assert.fail();
		}
	}

	@Test
	public void test_copyUtil02() {
		try {
			this.o2 = ((O2) CopyUtil02.getBean(this.o2, this.o1));
			System.out.println("CopyUtil02单元测试");
			System.out.println("-------------源对象o1----------------");
			showObjectPropertyValue(this.o1);
			System.out.println("-------------目标对象o2----------------");
			showObjectPropertyValue(this.o2);
			System.out.println();
		} catch (Exception e) {
			Assert.fail();
		}
	}

	@Test
	public void test_copyUtil03_copyProperties() {
		try {
			CopyUtil03.copyProperties(this.o1, this.o2);
			System.out.println("CopyUtil03的copyProperties方法单元测试");
			System.out.println("-------------源对象o1----------------");
			showObjectPropertyValue(this.o1);
			System.out.println("-------------目标对象o2----------------");
			showObjectPropertyValue(this.o2);
			System.out.println();
		} catch (Exception e) {
			Assert.fail();
		}
	}

	@Test
	public void test_copyUtil03_copyPropertiesExclude() {
		try {
			String[] excludsArray = new String[1];
			excludsArray[0] = "aac001";
			CopyUtil03.copyPropertiesExclude(this.o1, this.o2, excludsArray);
			System.out.println("CopyUtil03的copyPropertiesExclude方法单元测试");
			System.out.println("-------------源对象o1----------------");
			showObjectPropertyValue(this.o1);
			System.out.println("-------------目标对象o2----------------");
			showObjectPropertyValue(this.o2);
			System.out.println();
		} catch (Exception e) {
			Assert.fail();
		}
	}

	@Test
	public void test_copyUtil03_copyPropertiesInclude() {
		try {
			String[] includsArray = new String[2];
			includsArray[0] = "aac001";
			includsArray[1] = "ddd001";
			CopyUtil03.copyPropertiesInclude(this.o1, this.o2, includsArray);
			System.out.println("CopyUtil03的copyPropertiesInclude方法单元测试");
			System.out.println("-------------源对象o1----------------");
			showObjectPropertyValue(this.o1);
			System.out.println("-------------目标对象o2----------------");
			showObjectPropertyValue(this.o2);
		} catch (Exception e) {
			Assert.fail();
		}
	}

	private void showObjectPropertyValue(Object o) throws Exception {
		for (PropertyDescriptor property : Introspector.getBeanInfo(
				o.getClass(), Object.class).getPropertyDescriptors())
			System.out.println(property.getReadMethod()
					.invoke(o, new Object[0]));
	}
}

 单元测试输出结果:

CopyUtil01单元测试
-------------源对象o1----------------
00007
国产零零零零七
3.141592653589793
null
-------------目标对象o2----------------
00007
国产零零零零七
null
null
3.141592653589793

CopyUtil02单元测试
-------------源对象o1----------------
00007
国产零零零零七
3.141592653589793
null
-------------目标对象o2----------------
00007
国产零零零零七
null
null
3.141592653589793

CopyUtil03的copyProperties方法单元测试
-------------源对象o1----------------
00007
国产零零零零七
3.141592653589793
null
-------------目标对象o2----------------
00007
国产零零零零七
null
null
3.141592653589793

CopyUtil03的copyPropertiesExclude方法单元测试
-------------源对象o1----------------
00007
国产零零零零七
3.141592653589793
null
-------------目标对象o2----------------
null
国产零零零零七
null
null
3.141592653589793

CopyUtil03的copyPropertiesInclude方法单元测试
-------------源对象o1----------------
00007
国产零零零零七
3.141592653589793
null
-------------目标对象o2----------------
00007
null
null
null
3.141592653589793
    如上方式都是通过java的反射机制复制对象,不支持深度复制以及复制集合类型,但通用性会提高很多。 java本身提供了对象复制的能力,在java.lang.Object类中有clone方法,该方法是一个protected方法,在子类需要重写此方法并声明为public类型,而且还需实现Cloneable接口才能提供对象复制的能力,clone()是一个native方法,native方法的效率一般来说都是远高于java中的非native方法,对性能比较关心的话应考虑这种方式。
 
二.利用Spring实现属性之间的复制
    spring内部自有实现方法,如果我们需要在外面采用spring的托管复制,需要修改spring的源码,将spring中的org.springframework.beans.CachedIntrospectionResults类的forClass、getPropertyDescriptor、getBeanInfo改为可见的后重新打包。然后将Spring中关于复制的代码提取出来,最后修改成代码如下:
/**
 * 利用spring实现bean之间属性复制
 * @param source
 * @param target
 */
@SuppressWarnings("unchecked")
public static void copyPropertiesBySpring(Object source, Object target) throws Exception {
	Class actualEditable = target.getClass();
	PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
	for (int i = 0; i < targetPds.length; i++) {
		PropertyDescriptor targetPd = targetPds[i];
		if (targetPd.getWriteMethod() != null) {
			PropertyDescriptor sourcePd = getPropertyDescriptor(source
					.getClass(), targetPd.getName());
			if (sourcePd != null && sourcePd.getReadMethod() != null) {
				try {
					Method readMethod = sourcePd.getReadMethod();
					if (!Modifier.isPublic(readMethod.getDeclaringClass()
							.getModifiers())) {
						readMethod.setAccessible(true);
					}
					Object value = readMethod.invoke(source, new Object[0]);
					if(value == null)
						continue;
					//集合类判空处理
					if(value instanceof Collection) {
//							Collection newValue = (Collection)value;
//							if(newValue.size() <= 0)
							continue;
					}
					Method writeMethod = targetPd.getWriteMethod();
					if (!Modifier.isPublic(writeMethod.getDeclaringClass()
							.getModifiers())) {
						writeMethod.setAccessible(true);
					}
					writeMethod.invoke(target, new Object[] { value });
				} catch (Throwable ex) {
				}
			}
		}
	}
}
	
/**
 * 获取指定类指定名称的属性描述符
 * @param clazz
 * @param propertyName
 * @return
 * @throws BeansException
 */
@SuppressWarnings("unchecked")
public static PropertyDescriptor getPropertyDescriptor(Class clazz,
		String propertyName) throws BeansException {
	CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
	return cr.getPropertyDescriptor(propertyName);
}

/**
 * 获取指定类得所有属性描述符
 * @param clazz
 * @return
 * @throws BeansException
 */
@SuppressWarnings("unchecked")
public static PropertyDescriptor[] getPropertyDescriptors(Class clazz) throws BeansException {
	CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
	return cr.getBeanInfo().getPropertyDescriptors();
}
 
三.利用apache commons-beanutils的开源实现
    BeanUtils.copyProperties(dst, src)。方法能够将源对象和目标对象中相同名称的属性值复制过去。注意的是参数前面的是目标对象,后面是源对象。使用该方法需要注意:不能将入口方法与源对象、目标对象之一放在同一源文件之内,否者将没有任何效果。
    PropertyUtils.copyProperties(dst, src)。功能与BeanUtils.copyProperties类似,只是在同属性名称的类型参数之间可以执行转换操作。
 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值