拷贝工具BeanUtilsBean扩展
同事在使用ES的时候有这么样的需求:
- 拷贝对象时,如果源对象的属性为null,则不拷贝该属性到目标对象了;
- 源对象的属性不为null才会将属性拷贝到目标对象;
- 如果源对象的属性是自定义的bean,那么bean内的属性也按照1、2那样拷贝。
这时,使用BeanUtils的copyProperties就不满足要求,需要对该方法进行扩展。
解决上面问题的封装类CopyProperties
话不多说,上源码
java CopyProperties.java
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 拷贝对象属性工具
* 将源对象中不为空的属性拷贝到目标对象中,当目标对象的属性值为null时,会先为改属性创建一个对象,再将源对象的属性拷贝过去
* 注:不支持通过内部类定义属性,因为通过反射创建对象有问题
* @author wjy
*/
public class CopyProperties extends BeanUtilsBean {
private static CopyProperties INSTANCE = null;
private static byte[] lock = new byte[0];
private CopyProperties(){}
private static void init() {
synchronized (lock) {
if (null == INSTANCE) {
INSTANCE = new CopyProperties();
}
}
}
public static CopyProperties getInstance() {
if (null == INSTANCE) {
init();
}
return INSTANCE;
}
private Log log = LogFactory.getLog(CopyProperties.class);
//定义需要排掉的数据类型
private final List<String> exceptTypeList = Arrays.asList("Boolean", "Byte", "Character", "Short", "Integer", "Long", "Float", "Double", "String");
/**
* 如果源对象的值为null,不对目标对象对应属性做操作
* @param bean 目标对象
* @param name 属性名
* @param value 属性值
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
@Override
public void copyProperty(Object bean, String name, Object value) throws IllegalAccessException, InvocationTargetException {
if (value == null) {
return;
}
super.copyProperty(bean, name, value);
}
/**
* 拷贝属性方法
* @param dest 目标对象
* @param orig 源对象
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
@Override
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) */ {
//修改了目标对象为JavaBean类型时,拷贝属性的方法
PropertyDescriptor origDescriptors[] = getPropertyUtils().getPropertyDescriptors(orig);
for (int i = 0; i < origDescriptors.length; i++) {
String name = origDescriptors[i].getName();//对象的属性名称
String type = origDescriptors[i].getPropertyType().getSimpleName();//对象的属性类型
if ("class".equals(name)) {
continue; // No point in trying to set an object's class
}
if (!exceptTypeList.contains(type)) {
//过滤出没有被排掉的对象,也就是要处理的自定义对象
//System.out.println(type + " is not base data type");
Object deepDest = null;
try {
deepDest = getPropertyUtils().getProperty(dest, name);
}catch (NoSuchMethodException e){
; // Should not happen
}
//如果目标对象中的属性是null,需要为该属性new一个对象
if(deepDest == null){
Object obj = null;
try {
Class classType = dest.getClass().getField(name).getType();
obj = classType.newInstance();
}catch (NoSuchFieldException e){
; // create object fail
}catch (InstantiationException e){
; // create object fail
}
//反射创建对象失败,后面拷贝对象会报错
if(obj==null){
continue;
}
//将new的对象赋给dest对象
origDescriptors[i].getWriteMethod().invoke(dest,obj);
deepDest = obj;
}
//获取源中的深层对象
Object deepOrig = origDescriptors[i].getReadMethod().invoke(orig);
if(deepOrig == null){
System.out.println("No deep origin bean specified");
continue;
}
//递归的将源的深层对象赋给目标深层对象
copyProperties(deepDest,deepOrig);
continue;
}
if (getPropertyUtils().isReadable(orig, name) && getPropertyUtils().isWriteable(dest, name)) {
try {
Object value = getPropertyUtils().getSimpleProperty(orig, name);
//将值赋给目标对象名为name的属性
copyProperty(dest, name, value);
} catch (NoSuchMethodException e) {
; // Should not happen
}
}
}
}
}
}
Man.java
/**
* 男人:姓名、年龄、性别、家庭
* @author wjy
*/
public class Man {
public String name;
public Integer age;
public Boolean sex;
public Family family;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getSex() {
return sex;
}
public void setSex(Boolean sex) {
this.sex = sex;
}
public Family getFamily() {
return family;
}
public void setFamily(Family family) {
this.family = family;
}
@Override
public String toString() {
return "Man{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", family=" + family +
'}';
}
}
Family.java
/**
* 家庭:老婆、孩子
* @author wjy
*/
public class Family {
public Family(){}
public String wife;
public String child;
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public String getChild() {
return child;
}
public void setChild(String child) {
this.child = child;
}
@Override
public String toString() {
return "Family{" +
"wife='" + wife + '\'' +
", child='" + child + '\'' +
'}';
}
}
CopyTest.java
/**
* 测试类
* @author wjy
*/
public class CopyTest {
public static void main(String[] args){
Man man = new Man();
man.setName("Rick");
man.setSex(true);
Family fam = new Family();
fam.setWife("Ann");
man.setFamily(fam);//下文会注释掉的代码段1.
Man man1 = new Man();
man1.setName("Frank");
man1.setAge(20);
Family fam1 = new Family();
fam1.setWife("Carry");
fam1.setChild("Wade");
man1.setFamily(fam1);//下文会注释掉的代码段2.
System.out.println("origin man:"+man);
System.out.println("dest man1:"+man1);
CopyProperties cp = CopyProperties.getInstance();
try{
cp.copyProperties(man1,man);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("copy man to man1:"+man1);
}
}
运行结果
当代码1跟代码2都不注释时,会将man中非null的属性赋值到man1对应的属性。
当只有代码1注释时,会将man中非null的属性赋值到man1对应的属性。
由于family属性是null,也所以不会对man1中family的属性进行修改。
当只有代码2注释时,会将man中非null属性赋值到man1对应的属性。
由于man1的family属性是null,因此会先为man1创建一个family对象,再将man的family的非空属性赋值给man1的family的对应属性。
当代码1跟代码2都注释掉时,会将man中非null的属性复制到man1对应的属性。
但是man、man1的family属性都是null,因此man1的family只会创建一个family对象,但是不会被man的family属性覆盖掉。
如果有写的不好的地方欢迎拍砖
源码地址:https://github.com/weijiayou/JavaUtil_CopyProperties