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));
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
对象类型不一样 结果为空
@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));
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);
List泛型转换
@Data
public class CommonBeanListSource {
private Date testDate;
private List<Integer> ids;
}
@Data
public class CommonBeanListTarget {
private Date testDate;
private List<String> ids;
}
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));
6.org.mapstruct.Mapper
- MapStruct 是一个代码生成器,主要用于 Java Bean 之间的映射,如 entity 到 DTO 的映射。
- 灵活性高支持简单,复杂,嵌套,自定义扩展等多种手段
- 编译期生成,没有效率问题
- 方便发现问题,方便debug
- 缺点:不利于重构,比如变量名字由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.性能对比
8.总结
- 不同的Bean Copy工具,功能存在不同;同一个工具,不同版本实现可能存在些许差异,复杂对象copy要慎重
- 从性能来讲,Apache BeanUtils.copyProperties存在性能问题,其他工具性能可用
- mapStruct性能不过,使用也比较方便,代码也会比较清晰,可以考虑引入使用,感觉很不错
9.参考
- MapStruct – Java bean mappings, the easy way!(mapstruct官网)