关于BeanUtils.copyProperties(source, target)的使用

BeanUtils.copyProperties

首先,使用的是org.springframework.beans.BeanUtils;

source 来源, target 目标

顾名思义, BeanUtils.copyProperties(source, target); 第一个参数是需要拷贝的目标,第二个参数是拷贝后的目标

因为这个方法有很多种情况,容易分不清,所以今天测了一下不同情况下的结果如何。

1.target里面有source里没有的属性

并且此属性有值时

2.target和source相同属性的值不一样时

下面是没有拷贝之前的值

拷贝之后

可以看到,target里面不同值并没有清空,而是保留了下来。而相中属性本身存在的值被覆盖。

3.当target和source里面的属性名相同而类型不同时

拷贝之后

类型不同的属性无法拷贝。

Spring自带BeanUtils.copyProperties(Object source, Object target)之坑

在java服务化项目中,客户端和服务端之间交互经常用到BeanCopy,其目的是为了方便类之间的赋值,简单方便,但是经常会遇到复合对象赋值不上去的情况,究其原因是对BeanUtils.copyProperties(Object source, Object target)方法底层源码的不了解导致的,下面我来一步一步解释其原因。

先看一个例子:

@Data
public class BdmTeamMonthNewStoreTopResult implements Serializable {
    private static final long serialVersionUID = -3251482519506276368L;
    /**
     * 排名列表
     */
    private List<BdmTeamMonthNewStoreTopInfo> topInfoList;

    /**
     * 我的排名信息
     */
    private BdmTeamMonthNewStoreTopMyInfo myTopInfo;

    @Override
    public String toString() {
        return ReflectionToStringBuilder.reflectionToString(this,
                ToStringStyle.SHORT_PREFIX_STYLE);
    }
}
@Data
public class MonthNewStoreTopInfoResponse implements Serializable {
    private static final long serialVersionUID = 4483822161951780674L;
    /**
     * 排名信息列表
     */
    private List<MonthNewStoreTopInfo> topInfoList;
    /**
     * 我的排名信息
     */
    private MonthNewStoreTopMyInfo myTopInfo;

    @Override
    public String toString() {
        return ReflectionToStringBuilder.reflectionToString(this,
                ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

当我们用BeanUtils.copyProperties(monthNewStoreTopResponse , bdmTeamMonthNewStoreTopResult )时会发现myTopInfo这个对象赋值为null,这是为什么呢?让我们来看一看源码:

//这是点进源码的第一段代码
  public static void copyProperties(Object source, Object target) throws BeansException {
    copyProperties(source, target, null, (String[]) null);
  }

  //这个才是copy的主代码
  private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
      @Nullable 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);
            }
          }
        }
      }
    }
  }

其中ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())这个校验起到了关键的作用,我们再进入这段代码的源码看一眼,源码如下:

public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) {
    Assert.notNull(lhsType, "Left-hand side type must not be null");
    Assert.notNull(rhsType, "Right-hand side type must not be null");
    if (lhsType.isAssignableFrom(rhsType)) {
      return true;
    }
    if (lhsType.isPrimitive()) {
      Class<?> resolvedPrimitive = primitiveWrapperTypeMap.get(rhsType);
      if (lhsType == resolvedPrimitive) {
        return true;
      }
    }
    else {
      Class<?> resolvedWrapper = primitiveTypeToWrapperMap.get(rhsType);
      if (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper)) {
        return true;
      }
    }
    return false;
  }

其中lhsType.isAssignableFrom(rhsType)判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。

如果是则返回 true;否则返回 false。

如果该 Class表示一个基本类型,且指定的 Class 参数正是该 Class 对象,则该方法返回 true;否则返回 false。

意思其实就是说lhsType是不是rhsType的子类,如果是,则返回true,否则返回false。

这也就是说我们上面的例子MonthNewStoreTopMyInfo 对象和我们将要赋值的对象BdmTeamMonthNewStoreTopMyInfo 是同一个对象或者是它的子类才可以copy赋值成功,否则直接跳过返回了,不进行writeMethod.invoke(target, value)赋值;

哪为什么topInfoList却能赋值成功呢?

因为在lhsType.isAssignableFrom(rhsType)校验的时候是判断的是List类型的子类而不是List<BdmTeamMonthNewStoreTopMyInfo>中的BdmTeamMonthNewStoreTopMyInfo的子类。

所以我们在用Spring自带BeanUtils.copyProperties(Object source, Object target)进行对象copy时候需要特别注意,如果变量为非java自带的对象类型,则需要注意复合对象中的变量对象和被拷贝变量对象是同类型才可以。

如果MonthNewStoreTopMyInfo 和BdmTeamMonthNewStoreTopMyInfo不是同一个类型,可以通过先获得MonthNewStoreTopMyInfo 这个对象再和需要赋值的对象BdmTeamMonthNewStoreTopMyInfo进行变量级别的调用BeanUtils.copyProperties(MonthNewStoreTopMyInfo , BdmTeamMonthNewStoreTopMyInfo),最后再把复制后的结果set进结果集。

最好的解决办法是创建一个公共的model对象,替换MonthNewStoreTopMyInfo和BdmTeamMonthNewStoreTopMyInfo,这样也少创建了一个类,同时也减少了代码量,维护一份model,当有新增需求变化时,只需要修改公共的model对象即可,简单方便。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 解决BeanUtils.copyProperties不支持复制集合的问题

    工作中,经常使用Spring的工具类BeanUtils.copyProperties对bean属性进行复制,这里的复制属于浅复制.且不能复制集合和数组.本文会对该工具进行一些测试. 文末会提出复制集合属性的解决方案 准备工作:准备测试需要的类 @Data public class Class { private People[] member; private People teacher; private List<People> student; } @Data @NoArgsConstr

  • 解决Beanutils.copyproperties实体类对象不一致的问题

    今天给大家分析一个解决Beanutils.copyproperties实体类对象名不一致的解决方法,一般我们在两个对象拷贝的问题上,我个人用的比较多的就是Beanutils.copyproperties,字段名如果不一致的话就去实体类中使用重载,把当前实体类的对象赋值给另外一个对象,也有用到set(),当然这些也都能解决Beanutils.copyproperties实体类属性不一致的问题,不过今天要给大家分享的是,不用set()和实体类的重构,使用类的反射机制去完成! 话不多说直接开始: 我是

  • 解决BeanUtils.copyProperties无法成功封装的问题

    BeanUtils.copyProperties无法封装 使用 BeanUtils.copyProperties(user, memeber); 两个类中字段一样,但个别字段无法封装. 期初以为或许是字段的属性不同,仔细检查过还是一样,最后发现,是get.set方法名不同的原因. 如下 user里面有个字段为abc,他的get方法名为getABC(); member里面同样的字段abc,他的get方法名为getAbc(); 最后导致abc的字段无法成功封装 BeanUtils.copyPrope

  • 基于Spring BeanUtils的copyProperties方法使用及注意事项

    如下所示: package com.demo; import lombok.Data; import org.springframework.beans.BeanUtils; import java.util.Arrays; import java.util.List; /** * @author xiaobu * @version JDK1.8.0_171 * @date on 2019/10/8 10:04 * @description */ public class BeanUtilsTe

  • java使用BeanUtils.copyProperties踩坑经历

    1. 原始转换 提起对象转换,每个程序员都不陌生,比如项目中经常涉及到的DO.DTO.VO之间的转换,举个例子,假设现在有个OrderDTO,定义如下所示: public class OrderDTO { private long id; private Long userId; private String orderNo; private Date gmtCreated; // 省略get.set方法 } 有个OrderVO,定义如下所示: public class OrderVO { pr

  • BeanUtils.copyProperties在拷贝属性时忽略空值的操作

    BeanUtils.copyProperties忽略空值 使用spring开发的人,对这行代码肯定不陌生,常用于DTO.VO.PO之间的复制. /** * 全属性copy对象 * **/ BeanUtils.copyProperties(Object source, Object target) 但这行代码会将所有的属性都进行copy,有的时候我们想要个别属性不进行复制(比如:null值属性),这时就需要用到另一个方法: /** * 忽略某些属性copy对象 * **/ BeanUtils.co

  • java Beanutils.copyProperties( )用法详解

    这是一篇开发自辩甩锅稿~~~~ 昨天测试小姐姐将我的一个bug单重开了,emmmm....内心OS:就调整下对象某个属性类型这么简单的操作,我怎么可能会出错呢,一定不是我的锅!!but再怎么抗拒,bug还是要改的,毕竟晚上就要发版本了~~ 老老实实将我前天改的部分跟了一遍,恩,完美,没有任何的缺失~~but本应success的测试数据,接口返还的结果确实是false来着,那还是老老实实debug吧. 一步步跟下来,恩,多么顺畅,就说一定不是我的锅~~诶?不对不对,这里的ID值,为啥是null?传

  • BeanUtils.copyProperties扩展--实现String转Date

    BeanUtils.copyProperties(target,source)和PropertyUtils.copyProperties(target,source)都能将源对象的属性的值拷贝到目标对象相同属性名中. 区别在于: BeanUtils.copyProperties(target,source) 支持基础类型.String.java.sql.Date.java.sql.Timestamp.java.sql.Time之间的类型转换,即只要这些类型的属性名相同那么拷贝就能成功.但是会默认

  • 如何使用BeanUtils.copyProperties进行对象之间的属性赋值

    1.使用org.springframework.beans.BeanUtils.copyProperties方法进行对象之间属性的赋值,避免通过get.set方法一个一个属性的赋值 /** * 对象属性拷贝 <br> * 将源对象的属性拷贝到目标对象 * * @param source 源对象 * @param target 目标对象 */ public static void copyProperties(Object source, Object target) { try { BeanU

  • 关于BeanUtils.copyProperties(source, target)的使用

    BeanUtils.copyProperties 首先,使用的是org.springframework.beans.BeanUtils; source 来源, target 目标 顾名思义, BeanUtils.copyProperties(source, target); 第一个参数是需要拷贝的目标,第二个参数是拷贝后的目标. 因为这个方法有很多种情况,容易分不清,所以今天测了一下不同情况下的结果如何. 1.target里面有source里没有的属性 并且此属性有值时 2.target和sou

  • BeanUtils.copyProperties使用总结以及注意事项说明

    目录 1.前言 2.一般使用 3.拷贝属性时忽略空值 4.使用注意事项(1) 5.使用注意事项(2) 6.使用注意事项(3) 1.前言 开发过程中,讲一个对象的属性和值赋值到另一个对象上,大量使用了get.set方法,看着很臃肿,思考下肯定不只有我有这种想法,所以技术上肯定有方法能解决这个问题,所以查阅了一些资料发现了BeanUtils.copyProperties这个方法以下是这次所有的总结以及使用时的注意事项. 使用org.springframework.beans.BeanUtils.co

  • BeanUtils.copyProperties()所有的空值不复制问题

    目录 BeanUtils.copyProperties()所有的空值不复制 第一种情况 第二种情况 BeanUtils.copyProperties()的用法和注意点 属性为null也会被复制,内部类不会复制过去 注意点一 注意点二 BeanUtils.copyProperties()所有的空值不复制 第一种情况 所有为空值的属性都不copy 直接上代码吧~ public class UpdateUtil { /** * 所有为空值的属性都不copy * * @param source * @p

  • 关于两个BeanUtils.copyProperties()的用法及区别

    目录 两个BeanUtils.copyProperties()用法及区别 使用Beanutils.copyProperties()遇到的问题 两个BeanUtils.copyProperties()用法及区别 这两个类在不同的包下面,而这两个类的copyProperties()方法里面传递的参数赋值是相反的. 例如: a,b为对象 BeanUtils.copyProperties(a, b); public static void copyProperties(Object source, Ob

  • BeanUtils.copyProperties复制属性失败的原因及解决方案

    目录 BeanUtils.copyProperties复制属性失败 描述 解决办法 BeanUtils.copyProperties应用的改进 为解决这个问题我重写了部分spring BeanUtils的代码 BeanUtils.copyProperties复制属性失败 描述 在JavaE中使用 BeanUtils.copyProperties,把A对象的name.age等属性复制到B对象中,A与B对象的类型不同.出现的问题是复制属性失败,根本原因是 BeanUtils找不到set.get方法.

  • BeanUtils.copyProperties()参数的赋值顺序说明

    目录 BeanUtils.copyProperties()参数的赋值顺序 BeanUtils.copyProperties初体验,及其参数含义解释 用处 案例: 创建一个源类:source 创建一个目标target源类 创建测试类test ignoreProperties参数 案例 案例测试 BeanUtils.copyProperties()参数的赋值顺序 BeanUtils.copyProperties(x,y)有两个不同的jar包,引入不同的包,赋值的顺序不一样. 分别是: org.spr

随机推荐