对象拷贝类PropertyUtils,BeanUtils,BeanCopier

对BeanCopier拷贝对象中List类型属性的思考 背景 最近开发接口过程中,发现在使用BeanCopier拷贝对象的时候,当对象中嵌套自定义对象类型属性的时候,如果对象名称一致,但是对象类型不一致的时候,拷贝的时候,该属性是会被忽略的,但是当对象中嵌套List集合类型,集合中的类型是不同的对象类型,使用BeanCopier拷贝之后,返回给前台的数据是正确的,感觉不太懂其中的原理,就测试了下。 测试过程 新建几个对象 @Data @NoArgs... 阅读详情

业务系统中经常需要两个对象进行属性的拷贝,不能否认逐个的对象拷贝是最快速最安全的做法,但是当数据对象的属性字段数量超过程序员的容忍的程度,代码因此变得臃肿不堪,使用一些方便的对象拷贝工具类将是很好的选择。

目前流行的较为公用认可的工具类:

Apache的两个版本:(反射机制)

org.apache.commons.beanutils.PropertyUtils.copyProperties(Object dest, Object orig)

org.apache.commons.beanutils.BeanUtils.copyProperties(Object dest, Object orig)

Spring版本:(反射机制)

org.springframework.beans.BeanUtils.copyProperties(Object source, Object target, Class editable, String[] ignoreProperties)

cglib版本:(使用动态代理,效率高)

net.sf.cglib.beans.BeanCopier.copy(Object paramObject1, Object paramObject2, Converter paramConverter)

原理简介反射类型:(apache)

都使用静态类调用,最终转化虚拟机中两个单例的工具对象。

public BeanUtilsBean()

{

  this(new ConvertUtilsBean(), new PropertyUtilsBean());

}

ConvertUtilsBean可以通过ConvertUtils全局自定义注册。

ConvertUtils.register(new DateConvert(), java.util.Date.class);

PropertyUtilsBean的copyProperties方法实现了拷贝的算法。

1、  动态bean:orig instanceof DynaBean:Object value = ((DynaBean)orig).get(name);然后把value复制到动态bean类

2、  Map类型:orig instanceof Map:key值逐个拷贝

3、  其他普通类::从beanInfo【每一个对象都有一个缓存的bean信息,包含属性字段等】取出name,然后把sourceClass和targetClass逐个拷贝

Cglib类型:BeanCopier

copier = BeanCopier.create(source.getClass(), target.getClass(), false);

copier.copy(source, target, null);

 

Create对象过程:产生sourceClass-》TargetClass的拷贝代理类,放入jvm中,所以创建的代理类的时候比较耗时。最好保证这个对象的单例模式,可以参照最后一部分的优化方案。

创建过程:源代码见jdk:net.sf.cglib.beans.BeanCopier.Generator.generateClass(ClassVisitor)

1、  获取sourceClass的所有public get 方法-》PropertyDescriptor[] getters

2、  获取TargetClass 的所有 public set 方法-》PropertyDescriptor[] setters

3、  遍历setters的每一个属性,执行4和5

4、  按setters的name生成sourceClass的所有setter方法-》PropertyDescriptor getter【不符合javabean规范的类将会可能出现空指针异常】

5、  PropertyDescriptor[] setters-》PropertyDescriptor setter

6、  将setter和getter名字和类型 配对,生成代理类的拷贝方法。

Copy属性过程:调用生成的代理类,代理类的代码和手工操作的代码很类似,效率非常高。

 

缺陷预防

 

你不知道这些陷阱吧?

 

陷阱条件

Apache- PropertyUtils

Apache- BeanUtils

Spring-  BeanUtils

Cglib-

BeanCopier

是否可以扩展

useConvete功能

NO

Yes

Yes

Yes,但比较难用

(sourceObject,targetObject)的顺序

逆序

逆序

OK

 

OK

对sourceObject特殊属性的限制:(Date,BigDecimal等)【见备注1】

OK

NO,异常出错

OK

OK

相同属性名,且类型不匹配时候的处理

【见备注2】

异常,拷贝部分属性,非常危险

OK,并能进行初级转换,Long和Integer互转

异常,拷贝部分属性

OK,但是该属性不拷贝

Get和set方法不匹配的处理

【见备注3】

OK

OK

OK

创建拷贝的时候报错,无法拷贝任何属性(当且仅当sourceClass的get方法超过set方法)

 

 

备注1

 

对targetObject特殊属性的限制:(Date,BigDecimal等)

原因:dateTimeConveter的conveter没有对null值的处理

public class ErrorBeanUtilObject { //此处省略getter,setter方法

    private String name;

    private java.util.Date date;

}

 public class ErrorBeanUtilsTest {  

    public static void main(String args[]) throws Throwable  {  

    ErrorBeanUtilObject from = new ErrorBeanUtilObject(); 

    ErrorBeanUtilObject to = new ErrorBeanUtilObject();  

    //from.setDate(new java.util.Date());

    from.setName("TTTT");

    org.apache.commons.beanutils.BeanUtils.copyProperties(to, from);//如果from.setDate去掉,此处出现conveter异常

    System.out.println(ToStringBuilder.reflectionToString(from));

    System.out.println(ToStringBuilder.reflectionToString(to));

    }  

}

 

 

备注2

 

相同属性名,且类型不匹配时候的处理

原因:这两个工具类不支持同名异类型的匹配 !!!【包装类Long和原始数据类型long是可以的】

public class TargetClass {  //此处省略getter,setter方法

    private Long num;  

    private String name;

}

public class TargetClass {  //此处省略getter,setter方法

    private Long num;

    private String name;

}

public class ErrorPropertyUtilsTest {        

    public static void main(String args[]) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException  {  

        SourceClass from = new SourceClass();  

        from.setNum(1);

        from.setName("name"); 

        TargetClass to = new TargetClass();  

        org.apache.commons.beanutils.PropertyUtils.copyProperties(to, from); //抛出参数不匹配异常

        org.springframework.beans.BeanUtils.copyProperties(from, to);

//抛出参数不匹配异常

        System.out.println(ToStringBuilder.reflectionToString(from));    

        System.out.println(ToStringBuilder.reflectionToString(to));  

    }  

}

 

 

备注3

 

Get和set方法不匹配的处理

public class ErrorBeanCopierTest {    

    /**

     * 从该用例看出BeanCopier.create的target.class 的每一个get方法必须有队形的set方法

     * @param args

     */

    public static void main(String args[]) {  

        BeanCopier copier = BeanCopier.create(UnSatifisedBeanCopierObject.class, SourceClass.class,false);

        copier = BeanCopier.create(SourceClass.class, UnSatifisedBeanCopierObject.class, false); //此处抛出异常创建 

    }  

}

class UnSatifisedBeanCopierObject {   

    private String name;

    private Long num;

    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

    public Long getNum() {

       return num;

    }

//  public void setNum(Long num) {

//     this.num = num;

//  }

}

 

 

 

 

优化方案

一些优化和改进

增强apache的beanUtils的拷贝属性,注册一些新的类型转换

public class BeanUtilsEx extends BeanUtils

{

  public static void copyProperties(Object dest, Object orig)

  {

    try

    {

      BeanUtils.copyProperties(dest, orig);

    } catch (IllegalAccessException ex) {

      ex.printStackTrace();

    } catch (InvocationTargetException ex) {

      ex.printStackTrace();

    }

  }

  static

  {

    ConvertUtils.register(new DateConvert(), java.util.Date.class);

    ConvertUtils.register(new DateConvert(), java.sql.Date.class);

    ConvertUtils.register(new BigDecimalConvert(), BigDecimal.class);

  }

}

将beancopier做成静态类,方便拷贝

public class BeanCopierUtils {

     public static Map<String,BeanCopier> beanCopierMap = new HashMap<String,BeanCopier>();

    

     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();

     }

}

修复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则忽略掉该字段的操作,这样就可以避免异常的发生。

BeanCopy坑到MapStruct技巧 Bean Copy 和 MapStruct 都是 Java 中用于对象之间复制属性的工具,它们可以大大简化代码的编写和维护。MapStruct 的性能更高,因为它在编译时生成代码,而 Bean Copy 需要使用反射机制获取对象的属性信息。但是MapStruct 的学习成本相对较高,因为需要了解注解的使用方法以及如何配置和生成代码。 阅读详情

相关推荐

亚马逊(Amazon ads)广告API申请步骤(已经更新完)

你如果是自己的店铺使用,就选择左边的、如果是授权的那种给别人用就选右边的。审核通过了才能进行下一步,我目前就是刚申请完,后续会持续更新!然后会调整到,下面的页面,选择Agency然后右上角点击创建。审核通过会发邮件到你的企业邮箱、时间大概是1-7天左右。输入对应的信息,点击提交审核,就可以开始申请了。1、广告平台跟卖家平台是分开的,这个概念要清楚。2、注册广告一定要用企业邮箱!回到首页,点击成为合作伙伴。注册了以后会跳转到广告。然后再点击加入合作伙伴。然后会弹出下面的页面。

阿泽的博客 5655

BeanCopier的使用

【代码】BeanCopier的使用。

weixin_43860634的博客 4005

UE4载具性能调优指南:从参数设置到实战优化

本文详细解析了UE4载具性能调优的关键技巧,从WheeledVehicle类构建到轮子参数、传动系统设置,再到物理属性与操控调校。通过实战案例分享,帮助开发者优化载具的速度、加速度等核心性能,提升游戏体验。特别适合需要精细调校载具物理特性的开发者参考。

weixin_29019241的博客 469

BeanCopier 对象拷贝

BeanCopier 对象拷贝 package com.xmsme.nicsp.monitor.util; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.springframework.cglib.beans.BeanCopier; /** * BeanCopier工具类 * * @author zwy */ public...

w329636271的专栏 1048

MATLAB实战:蓝牙GFSK调制解调全流程解析(附误码率优化技巧)

本文深入解析了蓝牙GFSK调制解调在MATLAB中的全流程实现,包括高斯滤波器设计、调制解调技术及误码率优化技巧。通过实战案例和代码示例,帮助工程师提升蓝牙通信系统的性能表现,特别是在降低误码率方面的有效方法。

weixin_29046035的博客 277

群晖NAS搭建Git Server避坑指南:从SSH配置到权限管理的完整流程

本文提供了一份详尽的群晖NAS搭建Git Server实战指南。文章从启用并安全配置SSH服务入手,深入解析了用户权限体系设计、SSH连接与仓库初始化、客户端SSH密钥配置等核心步骤,并提供了常见错误排查与生产环境加固方案,帮助开发者成功构建私有、安全的代码托管平台。

rock5的博客 420

Spring学习笔记(二)【CGLIB浅拷贝BeanCopier的使用和详解】

常用的bean拷贝工具类当中,主要有Apache提供的beanUtils、Spring提供的beanUtils、Cglib提供的beanCopier,性能上分析如下表所示

Vinjcent 7498

安检机图像处理避坑指南:从条纹校正到物质分类,那些论文里没写的工程细节

本文深入探讨了双能X射线安检机图像处理中的关键工程实践,从条纹校正到物质分类,揭示了论文中未提及的实用技巧。针对探测器校正、物质分类模糊、伪彩色系统设计等核心问题,提供了动态参数更新、空间上下文分析和自适应色彩映射等解决方案,助力提升安检系统的准确性和鲁棒性。

weixin_30325487的博客 887

对象拷贝类PropertyUtils,BeanUtils,BeanCopier的技术沉淀

功能简介   对象拷贝的应用现状简介: 业务系统中经常需要两个对象进行属性的拷贝,不能否认逐个的对象拷贝是最快速最安全的做法,但是当数据对象的属性字段数量超过程序员的容忍的程度,代码因此变得臃肿不堪,使用一些方便的对象拷贝工具类将是很好的选择。 目前流行的较为公用认可的工具类: Apache的两个版本:(反射机制) org.apache.commons.beanutils.Pr

专注、专心、专业 9969

性能优化-高通cDSP(QDSP6)架构详解

高通cDSP开发的商业价值30秒快速认识高通cDSP开发的商业价值【高通cDSP旨在处理一些简单但需要大量计算的场景,如图像处理、科学计算、AI训练、计算机视觉等。这些场景使用CPU来运算会极大的降低CPU的吞吐量,因此利用现代寄存器较宽的特性设计出了适配数据并行的DSP芯片,cDSP就是典型的代表。利用cDSP+CPU既可以满足一般的使用场景,也可以完成现代图像和视频数据的处理。

发狂的小花的博客 5943

【XCharts插件】5-2、从Excel中读取数据并更新图表案例(v3.0)

XCharts插件是一款基于UGUI的功能强大、易用、参数可配置的数据可视化图表插件。【Unity3D插件】XCharts插件完全教程《全网最全》-专栏文章目录【XCharts插件】5-1、从Json中读取数据并更新图表案例(v3.0)介绍了如何从Json文件中读取数据,并更新表格,这篇文章就介绍如何从Excel中读取数据,并更新图表吧。

优快云博客专家,Unity3D高级软件工程师,《Unity 3D从入门到实战》作者。 1274
上一篇: 少有人走的路
下一篇: Java语言中的路径基础知识(总是忘,记录下)
奋斗的兔儿
博客等级 码龄18年 12粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值