在做业务的时候,我们有时为了隔离变化,会将DAO查询出来的Entity,和对外提供的DTO隔离开来。大概90%的时候,它们的结构都是类似的,但是我们很不喜欢写很多冗长的b.setF1(a.getF1())这样的代码,于是我们需要BeanCopier来帮助我们。BeanCopier其实已经有很多开源版本,例如DozerMapper、Apache BeanUtils、Spring、Jodd BeanUtils甚至是Cglib都提供了这样的功能。下面介绍Cglib的BeanCopier的使用。

1、通过拷贝bean对象来测试BeanCopier的基本使用和特性
Java代码 收藏代码
public class OrderEntity {
private int id;
private String name;
// Getters and setters are omitted
}

Java代码 收藏代码
public class OrderDto {
private int id;
private String name;
// Getters and setters are omitted
}

Java代码 收藏代码
public class PropWithDiffType {
private Integer id;
private String name;
// Getters and setters are omitted
}

Java代码 收藏代码
public class LackOfSetter {
private int id;
private String name;

public LackOfSetter() {
}  public LackOfSetter(int id, String name) {  this.id = id;  this.name = name;
}
// Getters and setters are omitted
// public void setName(String name) {
//  this.name = name;
// }

}

  1. 属性名称、类型都相同:
    Java代码 收藏代码
    @Test
    public void normalCopyTest() {
    OrderEntity entity = new OrderEntity();
    entity.setId(1);
    entity.setName(“orderName”);
    final BeanCopier copier = BeanCopier.create(OrderEntity.class, OrderDto.class, false);
    OrderDto dto = new OrderDto();
    copier.copy(entity, dto, null);
    Assert.assertEquals(1, dto.getId());
    Assert.assertEquals(“orderName”, dto.getName());
    }

结论:拷贝OK。
2. 属性名称相同、类型不同:

Java代码 收藏代码
@Test
public void sameNameDifferentTypeCopyTest() {
OrderEntity entity = new OrderEntity();
entity.setId(1);
entity.setName(“orderName”);
final BeanCopier copier = BeanCopier.create(OrderEntity.class, PropWithDiffType.class, false);
PropWithDiffType dto = new PropWithDiffType();
copier.copy(entity, dto, null);
Assert.assertEquals(null, dto.getId()); // OrderEntity的id为int类型,而PropWithDiffType的id为Integer类型,不拷贝
Assert.assertEquals(“orderName”, dto.getName());
}

结论:名称相同而类型不同的属性不会被拷贝。

注意:即使源类型是原始类型(int, short和char等),目标类型是其包装类型(Integer, Short和Character等),或反之:都不会被拷贝。

  1. 源类和目标类有相同的属性(两者的getter都存在),但目标类的setter不存在
    Java代码 收藏代码
    @Test
    public void targetLackOfSetterCopyTest() {
    OrderEntity entity = new OrderEntity();
    entity.setId(1);
    entity.setName(“orderName”);
    final BeanCopier copier = BeanCopier.create(OrderEntity.class, LackOfSetter.class, false); // 抛NullPointerException
    LackOfSetter dto = new LackOfSetter();
    copier.copy(entity, dto, null);
    }

结论:创建BeanCopier的时候抛异常。

导致异常的原因是BeanCopier类的第128~133行
Java代码 收藏代码
for (int i = 0; i < setters.length; i++) { // 遍历目标类的属性描述集
PropertyDescriptor setter = setters[i];
PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName()); // 从源类获取和目标类属性名称相同的属性描述
if (getter != null) {
MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod()); // 获取源类属性的getter方法
MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod()); // 获取目标类属性的setter方法。LackOfSetter类name属性的setter方法没有,所以报错

  1. 源类或目标类的setter比getter少
    Java代码 收藏代码
    @Test
    public void sourceLackOfSetterCopyTest() {
    LackOfSetter source = new LackOfSetter(1, “throne”);
    final BeanCopier copier = BeanCopier.create(LackOfSetter.class, OrderDto.class, false);
    OrderDto dto = new OrderDto();
    copier.copy(source, dto, null);
    Assert.assertEquals(1, dto.getId());
    Assert.assertEquals(“throne”, dto.getName());
    }

结论:拷贝OK。此时的setter多余,但不会报错。

总结:

  1. BeanCopier只拷贝名称和类型都相同的属性。

  2. 当目标类的setter数目比getter少时,创建BeanCopier会失败而导致拷贝不成功。
    二、自定义转换器
    当源和目标类的属性类型不同时,不能拷贝该属性,此时我们可以通过实现Converter接口来自定义转换器
    源类和目标类:

Java代码 收藏代码
public class AccountEntity {
private int id;
private Timestamp createTime;
private BigDecimal balance;
// Getters and setters are omitted
}

Java代码 收藏代码
public class AccountDto {
private int id;
private String name;
private String createTime;
private String balance;
// Getters and setters are omitted
}

  1. 不使用Converter
    Java代码 收藏代码
    public class BeanCopierConverterTest {

    @Test
    public void noConverterTest() {
    AccountEntity po = new AccountEntity();
    po.setId(1);
    po.setCreateTime(new Timestamp(10043143243L));
    po.setBalance(BigDecimal.valueOf(4000L));
    BeanCopier copier = BeanCopier.create(AccountEntity.class, AccountDto.class, false);
    AccountDto dto = new AccountDto();
    copier.copy(po, dto, null);
    Assert.assertNull(dto.getCreateTime()); // 类型不同,未拷贝
    Assert.assertNull(dto.getBalance()); // 类型不同,未拷贝
    }
    }

  2. 使用Converter
    基于目标对象的属性出发,如果源对象有相同名称的属性,则调一次convert方法:
    Java代码 收藏代码
    package net.sf.cglib.core;

public interface Converter {
// value 源对象属性,target 目标对象属性类,context 目标对象setter方法名
Object convert(Object value, Class target, Object context);
}
Java代码 收藏代码
@Test
public void converterTest() {
AccountEntity po = new AccountEntity();
po.setId(1);
po.setCreateTime(Timestamp.valueOf(“2014-04-12 16:16:15”));
po.setBalance(BigDecimal.valueOf(4000L));
BeanCopier copier = BeanCopier.create(AccountEntity.class, AccountDto.class, true);
AccountConverter converter = new AccountConverter();
AccountDto dto = new AccountDto();
copier.copy(po, dto, converter);
Assert.assertEquals(“2014-04-12 16:16:15”, dto.getCreateTime());
Assert.assertEquals(“4000”, dto.getBalance());
}

static class AccountConverter implements Converter {

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  @SuppressWarnings("rawtypes")
@Override
public Object convert(Object value, Class target, Object context) {  if (value instanceof Integer) {  return (Integer) value;  } else if (value instanceof Timestamp) {  Timestamp date = (Timestamp) value;  return sdf.format(date);  } else if (value instanceof BigDecimal) {  BigDecimal bd = (BigDecimal) value;  return bd.toPlainString();  }  return null;
}

}
注:一旦使用Converter,BeanCopier只使用Converter定义的规则去拷贝属性,所以在convert方法中要考虑所有的属性。
三、缓存BeanCopier提升性能
BeanCopier拷贝速度快,性能瓶颈出现在创建BeanCopier实例的过程中。 所以,把创建过的BeanCopier实例放到缓存中,下次可以直接获取,提升性能:
Java代码 复制代码 收藏代码
public class CachedBeanCopier {

static final Map<String, BeanCopier> BEAN_COPIERS = new HashMap<String, BeanCopier>();  public static void copy(Object srcObj, Object destObj) {  String key = genKey(srcObj.getClass(), destObj.getClass());  BeanCopier copier = null;  if (!BEAN_COPIERS.containsKey(key)) {  copier = BeanCopier.create(srcObj.getClass(), destObj.getClass(), false);  BEAN_COPIERS.put(key, copier);  } else {  copier = BEAN_COPIERS.get(key);  }  copier.copy(srcObj, destObj, null);
}  private static String genKey(Class<?> srcClazz, Class<?> destClazz) {  return srcClazz.getName() + destClazz.getName();
}

}

使用CGlib实现Bean拷贝(BeanCopier)相关推荐

  1. 6种常用Bean拷贝工具一览

    在我们日常的工作中,经常需要做对象的拷贝或转化,例如在传递参数时,把入参的DTO转化为PO存入数据库,在返回前端时把PO再转化为VO.如果再分的细一点,可能还会有DO(Domain Object),T ...

  2. java Bean拷贝忽略空属性

    java bean的拷贝一般采用两种办法 1.Spring里面的方法, org.springframework.beans.BeanUtils.copyProperties 2.Apache里面的方法 ...

  3. 使用Cglib的BeanCopier实现Bean的拷贝

    选择Cglib的BeanCopier进行Bean拷贝的理由是,其性能要比Spring的BeanUtils,Apache的BeanUtils和PropertyUtils要好很多,尤其是数据量比较大的情况 ...

  4. Bean的拷贝之BeanUtils

    本文来说下Bean的各类拷贝工具 文章目录 概述 对象拷贝 BeanUtils apache的BeanUtils spring的BeanUtils cglib BeanCopier Hutool Be ...

  5. cglib BeanCopier的使用

    一.概述 选择Cglib的BeanCopier进行Bean拷贝的理由是,其性能要比Spring的BeanUtils,Apache的BeanUtils和PropertyUtils要好很多,尤其是数据量比 ...

  6. 链式写法下,net.sf.cglib.beans.BeanCopier.copy NullPointerException的问题

    前言 由于MVC分层设计的理念,Java后端开发中,会经常面临各种DTO.DO.PO.VO的对象之间的转换.其中,pojo对象大部分字段名与字段类型一致,为了提供开发效率,避免过多收到set,一般开发 ...

  7. 【小工具】根据定义的白名单字段进行Bean的拷贝

    背景 Bean的拷贝一直有一些类可以使用,比如Apache的org.apache.commons.beanutils.BeanUtils或者Spring的org.springframework.bea ...

  8. cglib 的BeanCopier高性能解密

    在一些系统代码中,随处可见的BeanCopier(源于org.springframework.cglib.beans),它主要用在将PO与DTO互转.一些人在惊叹它的高性能的同时,不曾了解它的实现原理 ...

  9. java 实例对象拷贝,实例详解java对象拷贝

    这篇文章主要介绍了java对象拷贝详解及实例的相关资料,需要的朋友可以参考下 java对象拷贝详解及实例 Java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的:@T ...

最新文章

  1. Tcp方式采集CNC兄弟设备数据
  2. Caffe: Caffe的Python接口
  3. HH SaaS电商系统的供应商系统设计
  4. hp g6服务器安装系统,HPProLiantDL180G6服务器安装图.PDF
  5. string插入字符_String类
  6. python抓取贴吧_python抓取百度贴吧-校花吧,网页图片
  7. VMware view client for ipad 测试
  8. 并发编程学习(2)----volatile与synchronized
  9. 计算机的cpu故障,计算机cpu常见故障
  10. 用计算机求值根号12345,手算开根号
  11. spring MVC3 集成 freemarker
  12. Vue 3.0 Ref-sugar 提案到底是啥,真的是自寻死路吗?
  13. forward和include的区别详解
  14. 基于 Holt-Winters季节性预测模型 的时间序列预测
  15. 读文献——《Very Deep Convolutional Networks for Large-scale Image Recognition》
  16. python3大神器_Python三大神器之pip的安装
  17. QQRobot一款基于Java的娱乐qq机器人
  18. cad指北针lisp_用CAD里的LISP画图,题目如图片,编写一程序,可在任意位置任意方向绘制指定大小的指北针。...
  19. dna编码库_DNA编码分子库技术取得新进展
  20. ABBYY FineReader(OCR文字识别软件)14官方中文版下载

热门文章

  1. 锐捷网络与科大讯飞战略签约 强强联手助力智慧教育
  2. vue中的插槽(slot)
  3. 美丽修行的“数字化修行”记
  4. 总投资300亿,南山前海南山村旧改城市更新
  5. JS合并两个数组的方法介绍
  6. js事件循环机制(await-async-事件循环)
  7. Smarty foreach 循环次数 首次 末次
  8. Vivado调用Modelsim默认仿真条件设置
  9. JAVA面试题--分布式(最新最全)
  10. 荣耀v20会升级到Android10吗,荣耀v20该不该升级到emui10?有什么样的优势?