博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
cglib BeanCopier的使用
阅读量:6787 次
发布时间:2019-06-26

本文共 8350 字,大约阅读时间需要 27 分钟。

一、概述

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

  之前的一篇文章: 提到了Cglib的BeanCopier使用ASM字节码生成技术,所以性能会非常好。

  下面的文章内容直接整理自网上资源,有错误之处敬请谅解,后续再整理。

二、相关使用案例

1、引入maven依赖

asm
asm
3.3.1
asm
asm-commons
3.3.1
asm
asm-util
3.3.1
cglib
cglib-nodep
2.2.2

2、使用Demo

public class OrderEntity {      private int id;      private String name;      // Getters and setters are omitted  }
public class OrderDto {      private int id;      private String name;      // Getters and setters are omitted  }
public class PropWithDiffType {      private Integer id;      private String name;      // Getters and setters are omitted  }
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;      // }  }

2.1属性名称、类型都相同: 

@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.2属性名称相同、类型不同:

@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等),或反之:都不会被拷贝。 

2.3源类和目标类有相同的属性(两者的getter都存在),但目标类的setter不存在 

@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行 

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方法没有,所以报错

3、小结: 

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

2. 当目标类的setter数目比getter少时,创建BeanCopier会失败而导致拷贝不成功。

三、自定义Converter转换器

当源和目标类的属性类型不同时,不能拷贝该属性,此时我们可以通过实现Converter接口来自定义转换器

源类和目标类: 

public class AccountEntity {      private int id;      private Timestamp createTime;      private BigDecimal balance;      // Getters and setters are omitted  }
public class AccountDto {      private int id;      private String name;      private String createTime;      private String balance;      // Getters and setters are omitted  }

1、不使用Converter 

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方法: 

package net.sf.cglib.core;    public interface Converter {      // value 源对象属性,target 目标对象属性类,context 目标对象setter方法名      Object convert(Object value, Class target, Object context);  }
@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方法中要考虑所有的属性。

四、提供个工具类

package com.yusj.utils;    import java.util.HashMap;  import java.util.Map;    import net.sf.cglib.beans.BeanCopier;    /**  *   * 将beancopier做成静态类,方便拷贝  * 
创建日期:2015年12月1日 *
Copyright 2015 UTOUU All Rights Reserved * @author yushaojian * @since 1.0 * @version 1.0 */ public class CglibBeanCopierUtils { /** * */ public static Map
beanCopierMap = new HashMap
(); /** * @Title: copyProperties * @Description: TODO(bean属性转换) * @param source 资源类 * @param target 目标类 * @author yushaojian * @date 2015年11月25日下午4:56:44 */ public static void copyProperties(Object source,Object target){ String beanKey = generateKey(source.getClass(),target.getClass()); BeanCopier copier = null; if (!beanCopierMap.containsKey(beanKey)) { copier = BeanCopier.create(source.getClass(), target.getClass(), false); beanCopierMap.put(beanKey, copier); }else { copier = beanCopierMap.get(beanKey); } copier.copy(source, target, null); } private static String generateKey(Class
class1,Class
class2){ return class1.toString() + class2.toString(); } /*注: (1)相同属性名,且类型不匹配时候的处理,ok,但是未满足的属性不拷贝; (2)get和set方法不匹配的处理,创建拷贝的时候报错,无法拷贝任何属性(当且仅当sourceClass的get方法超过set方法时出现) (3)BeanCopier 初始化例子:BeanCopier copier = BeanCopier.create(Source.class, Target.class, useConverter=true) 第三个参数userConverter,是否开启Convert,默认BeanCopier只会做同名,同类型属性的copier,否则就会报错. copier = BeanCopier.create(source.getClass(), target.getClass(), false); copier.copy(source, target, null); (4)修复beanCopier对set方法强限制的约束 改写net.sf.cglib.beans.BeanCopier.Generator.generateClass(ClassVisitor)方法 将133行的 MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod()); 预先存一个names2放入 109 Map names2 = new HashMap(); 110 for (int i = 0; i < getters.length; ++i) { 111 names2.put(setters[i].getName(), getters[i]); } 调用这行代码前判断查询下,如果没有改writeMethod则忽略掉该字段的操作,这样就可以避免异常的发生。*/ }

 

 

 

 

 参考文章:

转载于:https://www.cnblogs.com/java-jun-world2099/articles/11022357.html

你可能感兴趣的文章
2018-2019-2 20165334-Exp6:信息收集与漏洞扫描
查看>>
python发邮件模板参考
查看>>
【转载】rpc.rstatd安装与配置
查看>>
新东方雅思词汇---10.1、(a)esthet
查看>>
html常用属性border-radius、linear-gradient怎么使用
查看>>
jquery-12 折叠面板如何实现(两种方法)
查看>>
bootstrap课程10 从外部引入视频到页面用什么标签
查看>>
m_Orchestrate learning system---二十一、怎样写算法比较轻松
查看>>
贪心3--删数问题
查看>>
handler消息机制入门
查看>>
二维数组
查看>>
第十周作业
查看>>
阅读笔记《构建之法》五
查看>>
SQL 高级查询
查看>>
LIUNX-Centos 7 编译GDAL
查看>>
日志、命名查询
查看>>
Google Chrome调试常用快捷键
查看>>
发送邮件那些事
查看>>
loadrunner参数化
查看>>
Topcoder SRM558 1000 SurroundingGame
查看>>