对象拷贝的应用现状简介:
业务系统中经常需要两个对象进行属性的拷贝,不能否认逐个的对象拷贝是最快速最安全的做法,但是当数据对象的属性字段数量超过程序员的容忍的程度,代码因此变得臃肿不堪,使用一些方便的对象拷贝工具类将是很好的选择。
目前流行的较为公用认可的工具类:
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; // } } |
一些优化和改进
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则忽略掉该字段的操作,这样就可以避免异常的发生。 |
相关推荐
标题中的“java对象复制克隆”主要涉及的是深拷贝,这是一种创建一个新对象的方式,新对象不仅复制了原对象的所有属性,还复制了嵌套的对象引用,使得修改复制后的对象不会影响到原始对象。 **浅拷贝**:在浅拷贝中...
- **浅拷贝**:创建一个新的对象,只复制对象的引用,不复制内部引用的对象。这意味着改变其中一个对象的内部引用对象,会影响另一个对象。 - **深拷贝**:创建一个全新的对象,不仅复制对象本身,还递归复制其...
深拷贝不仅复制对象,还复制对象中引用的对象。在描述中提到的方法,很可能是关于深拷贝的实现。 在Java中,我们可以通过以下几种方法实现对象的深拷贝: 1. **实现Serializable接口**:利用序列化和反序列化来...
1. **引用复制**:浅拷贝只复制对象的引用,而不是对象本身,所以改变副本不会影响原始对象,反之亦然,除非涉及到可变对象(如列表、字典)。 2. **深拷贝**:深拷贝会创建一个全新的对象,并递归复制嵌套的对象,...
JavaScript中的对象拷贝是编程中常见的操作,尤其在处理复杂数据结构时显得尤为重要。对象拷贝分为两种主要类型:浅拷贝和深拷贝。 浅拷贝是指创建一个新对象,新对象的属性引用了原对象中属性的引用。这意味着,当...
这里主要讨论两种类型的对象拷贝:浅拷贝和深拷贝。 浅拷贝,也称为表面拷贝,是创建一个新的对象,然后将原始对象中的基本类型字段的值复制到新对象中。对于引用类型字段,浅拷贝只会复制引用,而不是引用的对象...
Java对象复制是一个重要的概念,它涉及到Java中对象的深拷贝和浅拷贝。当我们需要一个与原有对象具有相同属性的新对象,但不希望修改新对象会影响原对象时,就需要进行对象复制。Java提供了多种复制对象的方式,其中...
对象复制是指将一个对象的属性值复制到另一个对象的过程,通常用于创建新对象或者初始化对象。`BeanUtilsBean.copyProperties()`方法是实现此功能的关键,它允许我们将一个JavaBean的所有属性值复制到另一个具有相同...
Java EE对象拷贝工具Dozer是一款强大的数据映射库,它简化了在Java对象之间进行深度复制的过程。在软件开发中,特别是在业务层处理数据时,我们经常需要将一个对象的属性值复制到另一个对象中,Dozer提供了一个优雅...
规则指出,如果需要自定义复制构造函数,也应自定义赋值运算符(拷贝赋值操作符)和析构函数,以确保正确管理资源。 4. **移动语义**:随着C++11的引入,移动构造函数和移动赋值运算符成为标准,它们优化了对象的...
在标题“编程语言java对象复制.pdf”和描述“编程语言java对象复制.pdf”中,明确指出了文档的主题是关于Java编程语言中对象复制的技术知识。在给出的内容部分中,通过具体的Java代码示例,讨论了在Java中实现对象...
### 软件定义存储中的对象复制服务 #### 一、对象复制的原理与机制 **对象复制**作为一项关键的数据保护技术,在软件定义存储(SDS)领域扮演着至关重要的角色。它允许用户在不同位置维护数据的多个副本,以此来提高...
在Java编程中,对象之间的复制是一项常见的操作,特别是在数据传输、模型转换或者持久化存储等场景下。这里的“java不同对象之间及集合的复制”主要关注如何高效且便捷地完成对象及其集合的复制工作,避免繁琐的手动...
在Java编程中,对象复制是一个常见的需求,特别是在软件开发中处理数据对象时。Java对象复制主要分为浅复制(Shallow Copy)和深复制(Deep Copy)。本文将深入探讨Java对象复制,特别是通过`clone()`方法实现的复制...
《基于CORBA的容错对象复制算法》一文深入探讨了在分布式计算环境中,如何利用CORBA(Common Object Request Broker Architecture)框架实现容错对象复制的技术细节与算法设计。CORBA作为一项成熟的企业级分布式计算...
在Java编程中,对象拷贝是常见的操作,主要分为两种类型:浅拷贝(Shallow Copy)和深拷贝(Deep Copy)。这两种拷贝方式在处理对象时有着本质的区别,对于理解对象复制机制以及在实际开发中正确地复制复杂对象至关...
Sigslot 是一个小型但功能强大的 C++ 开源库,专门用于实现信号(signals)和槽...通过修复信号对象拷贝的问题,它变得更加可靠,适用于更广泛的场景。在实际开发中,结合 sigslot 可以设计出更灵活、响应式的系统。
在JavaScript中,对象复制,也称为克隆,是一项关键的编程技巧,用于创建一个对象的新副本,而不会改变原始对象。JavaScript的浅复制和深复制是两个重要的概念,它们在处理复杂数据结构时尤为关键。 浅复制...
在Java编程中,对象复制是一个常见的需求,尤其是在处理复杂数据结构时。Java对象的复制主要有两种方式:浅复制(Shallow Copy)和深复制(Deep Copy)。标题中的"java对象复制"主要涉及到Java中对象复制的基本概念...