天天看点

拷贝工具BeanUtilsBean扩展拷贝工具BeanUtilsBean扩展

拷贝工具BeanUtilsBean扩展

同事在使用ES的时候有这么样的需求:

  1. 拷贝对象时,如果源对象的属性为null,则不拷贝该属性到目标对象了;
  2. 源对象的属性不为null才会将属性拷贝到目标对象;
  3. 如果源对象的属性是自定义的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[];

  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 = ; 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 = ; 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();
    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. 当代码1跟代码2都不注释时,会将man中非null的属性赋值到man1对应的属性。
    拷贝工具BeanUtilsBean扩展拷贝工具BeanUtilsBean扩展
  2. 当只有代码1注释时,会将man中非null的属性赋值到man1对应的属性。

    由于family属性是null,也所以不会对man1中family的属性进行修改。

    拷贝工具BeanUtilsBean扩展拷贝工具BeanUtilsBean扩展
  3. 当只有代码2注释时,会将man中非null属性赋值到man1对应的属性。

    由于man1的family属性是null,因此会先为man1创建一个family对象,再将man的family的非空属性赋值给man1的family的对应属性。

    拷贝工具BeanUtilsBean扩展拷贝工具BeanUtilsBean扩展
  4. 当代码1跟代码2都注释掉时,会将man中非null的属性复制到man1对应的属性。

    但是man、man1的family属性都是null,因此man1的family只会创建一个family对象,但是不会被man的family属性覆盖掉。

    拷贝工具BeanUtilsBean扩展拷贝工具BeanUtilsBean扩展

如果有写的不好的地方欢迎拍砖

源码地址:https://github.com/weijiayou/JavaUtil_CopyProperties