1.报错现象

sed by: org.apache.dubbo.remoting.RemotingException: Failed to send response: Response [id=4321, version=2.0.2, status=20, event=false, error=null, result=AppResponse [value=RecommendAtConnectRes(messageBase=MessageBaseRes(pubList=null, noteList=null, tipList=[PushBaseDto(ct=null, content=机器人冰冰为您服务)], saveList=null)), exception=null]], cause: java.lang.RuntimeException: Serialized class com.dewu.kefu.bot.customer.domain.dto.PushBaseDto must implement java.io.Serializable
 Java field: private java.util.List MessageBase.tipList
 Java field: private .base.MessageBaseRes RecommendAtConnectRes.messageBase
java.lang.RuntimeException: Serialized class .PushBase must implement java.io.Serializable
 Java field: private java.util.List base.MessageBase.tipList
        at com.alibaba.com.caucho.hessian.io.JavaSerializer$FieldSerializer.serialize(JavaSerializer.java:304)
        at com.alibaba.com.caucho.hessian.io.JavaSerializer.writeInstance(JavaSerializer.java:284)
        at com.alibaba.com.caucho.hessian.io.JavaSerializer.writeObject(JavaSerializer.java:251)
        at com.alibaba.com.caucho.hessian.io.Hessian2Output.writeObject(Hessian2Output.java:412)
        at org.apache.dubbo.common.serialize.hessian2.Hessian2ObjectOutput.writeObject(Hessian2ObjectOutput.java:97)
        at org.apache.dubbo.rpc.protocol.dubbo.DubboCodec.encodeResponseData(DubboCodec.java:203)
        at org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.encodeResponse(ExchangeCodec.java:283)
        at org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.encode(ExchangeCodec.java:71)
MessageBase  messageBase  = getRecommendContent(request)
//返回给远程接口的对象
MessageBaseRes messageBaseRes = new MessageBaseRes();
BeanUtil.copyProperties(messageBase, messageBaseRes);

@Data
public class PushBase {
    private String ct;
    private String content;
}

@Data
public class MessageBase {
    private List<PushBase> tipList;
}

@Data //远程接口返回对象
public class PushBaseRes implements Serializable {
    private static final long serialVersionUID = 8434107544771588426L;
    private String ct;
    private String content;
}

@Data  //远程接口返回对象
public class MessageBaseRes implements Serializable {
    private static final long serialVersionUID = -6492447071340256545L;
    private List<PushBaseRes> tipList;
}

2.cn.hutool.core.bean.BeanUtil (5.1.0)

属性泛型处理:TargetBean拷贝的成员属性实际类型可能跟声明不一致

@Data
public class HuUnsameListSource {
    private Date testDate;
    private List<Integer> ids;
}

@Data
public class HuUnsameListTarget {
    private Date testDate;
    private List<String> ids;
}

HuUnsameListSource source = new HuUnsameListSource();
source.setTestDate(new Date());
source.setIds(Arrays.asList(1, 2, 3));

HuUnsameListTarget target = new HuUnsameListTarget();
BeanUtil.copyProperties(source,target);
System.out.println(JSON.toJSON(target));

对象类型不一样属性值可以复制

@Data
public class HuUnSameInnerSource {
    private Date testDate;
    private Long id;
}

@Data
public class HuUnSameInnerTarget {
    private Date testDate;
    private String id;
}

HuUnSameInnerSource source = new HuUnSameInnerSource();
source.setTestDate(new Date());
source.setId(123L);
HuUnSameInnerTarget target = new HuUnSameInnerTarget();
BeanUtil.copyProperties(source,target);
System.out.println(JSON.toJSON(target));

java copy对象的工具类 空字段不复制_java

java copy对象的工具类 空字段不复制_intellij-idea_02

3.org.springframework.beans.BeanUtils

List copy 类型不一样 结果为空 (spring-beans-5.3.14.jar) 会判断属性的泛型是否一致,如不一致,直接忽略属性的拷贝

@Data
public class SpringListSource {
    private Date testDate;
    private List<Integer> ids;
}

@Data
public class SpringListTarget {
    private Date testDate;
    private List<String> ids;
}

SpringListSource source = new SpringListSource();
source.setTestDate(new Date());
source.setIds(Arrays.asList(1, 2, 3));
SpringListTarget target = new SpringListTarget();
BeanUtils.copyProperties(source,target);
System.out.println(target);

spring-beans-5.2.4.RELEASE

java copy对象的工具类 空字段不复制_java_03

对象类型不一样 结果为空

@Data
public class SpringUnSameInnerSource {
    private Date testDate;
    private Long id;
}

@Data
public class SpringUnSameInnerTarget {
    private Date testDate;
    private String id;
}

SpringUnSameInnerSource source = new SpringUnSameInnerSource();
source.setTestDate(new Date());
source.setId(123L);
SpringUnSameInnerTarget target = new SpringUnSameInnerTarget();
BeanUtils.copyProperties(source,target);
System.out.println(JSON.toJSON(target));

java copy对象的工具类 空字段不复制_Data_04

java copy对象的工具类 空字段不复制_jar_05

4.org.apache.commons.beanutils.BeanUtils(性能比较差,不推荐使用)

对于对象拷贝加了很多的检验,包括类型的转换,甚至还会检验对象所属的类的可访问性,可谓相当复杂,这也造就了它的差劲的性能

类型一样 转换类型

@Data
public class CommonUnsameInnerSource {
    private Date testDate;
    private Integer id;
}
@Data
public class CommonUnsameInnerTarget {
    private Date testDate;
    private String id;
}

CommonBeanListSource source = new CommonBeanListSource();
source.setTestDate(new Date());
source.setIds(Arrays.asList(1, 2, 3));
CommonBeanListTarget target = new CommonBeanListTarget();
BeanUtils.copyProperties(target,source);
System.out.println(target);

java copy对象的工具类 空字段不复制_jar_06

List泛型转换

@Data
public class CommonBeanListSource {
    private Date testDate;
    private List<Integer> ids;
}

@Data
public class CommonBeanListTarget {
    private Date testDate;
    private List<String> ids;
}

java copy对象的工具类 空字段不复制_List_07

5.net.sf.cglib.beans.BeanCopier

  • 基于CGlib字节码操作生成get、set方法
  • 整体性能很不错,使用也不复杂,可以使用
CglibTestSource source = new CglibTestSource();
source.setTestDate(new Date());
source.setIds(Arrays.asList(1, 2, 3));

final BeanCopier copier = BeanCopier.create(CglibTestSource.class, CglibTestTarget.class, false);
CglibTestTarget target = new CglibTestTarget();
copier.copy(source, target, null);
System.out.println(JSON.toJSON(target));

java copy对象的工具类 空字段不复制_jar_08

6.org.mapstruct.Mapper

  1. MapStruct 是一个代码生成器,主要用于 Java Bean 之间的映射,如 entity 到 DTO 的映射。
  2. 灵活性高支持简单,复杂,嵌套,自定义扩展等多种手段
  3. 编译期生成,没有效率问题
  4. 方便发现问题,方便debug
  5. 缺点:不利于重构,比如变量名字由a改成b,感知不到
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Person {
    private Long id;
    private String name;
    private String email;
    private Date birthday;
}

@NoArgsConstructor
@AllArgsConstructor
@Data
public class PersonDTO {
    private Long id;
    private String name;
    private String email;
    private Date birthday;
}

@Mapper
public interface DtoConverter {
    DtoConverter INSTANCE = Mappers.getMapper(DtoConverter.class);
    List<PersonDTO> domain2dto(List<Person> people);
}

List<Person> people = new ArrayList<>();
Person person = new Person();
person.setBirthday(new Date());
people.add(person);
List<PersonDTO> personDTOs = DtoConverter.INSTANCE.domain2dto(people);
assertNotNull(personDTOs);

//编译之后
package com.example.beancopydemo.mapstruct;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class DtoConverterImpl implements DtoConverter {
    public DtoConverterImpl() {
    }

    public List<PersonDTO> domain2dto(List<Person> people) {
        if (people == null) {
            return null;
        } else {
            List<PersonDTO> list = new ArrayList(people.size());
            Iterator var3 = people.iterator();

            while(var3.hasNext()) {
                Person person = (Person)var3.next();
                list.add(this.personToPersonDTO(person));
            }

            return list;
        }
    }

    protected PersonDTO personToPersonDTO(Person person) {
        if (person == null) {
            return null;
        } else {
            PersonDTO personDTO = new PersonDTO();
            personDTO.setId(person.getId());
            personDTO.setName(person.getName());
            personDTO.setEmail(person.getEmail());
            personDTO.setBirthday(person.getBirthday());
            return personDTO;
        }
    }
}

7.性能对比

java copy对象的工具类 空字段不复制_java_09

8.总结


  • 不同的Bean Copy工具,功能存在不同;同一个工具,不同版本实现可能存在些许差异,复杂对象copy要慎重
  • 从性能来讲,Apache BeanUtils.copyProperties存在性能问题,其他工具性能可用
  • mapStruct性能不过,使用也比较方便,代码也会比较清晰,可以考虑引入使用,感觉很不错

9.参考