Java对象拷贝备忘

列举

//cglib
net.sf.cglib.beans.BeanCopier.create 
net.sf.cglib.beans.BeanCopier.copy
//spring-beans
org.springframework.beans.BeanUtils.copyProperties
//commons-beanutils
org.apache.commons.beanutils.BeanUtils.copyProperties
org.apache.commons.beanutils.PropertyUtils.copyProperties

原理

以上4种方式都是通过自动调用原对象的getter方法,再用目标对象的setter方法设置进去,区别在于BeanCopier先通过create创建了一个以BeanCopier派生的动态代理类,其创建的copy方法实现包含以上所有getter和setter的调用过程,调用copy可以达到原生代码方式的性能;而其他所有方式如spring-beans和commons-beanutils的实现都是通过反射逐个调用getter和setter,性能相比前者较差。

细节

细节 Apache-PropertyUtils Apache-BeanUtils Spring-BeanUtils Cglib-BeanCopier
相同属性名,不同类型转换Converter扩展 NO Yes Yes Yes,较难用,需要对每一种属性类型都做转换
相同属性名,Integer和int的处理 OK OK OK 不拷贝,忽略该属性
相同属性名,Long和Integer的处理 异常报错 OK,自动互转 不拷贝,忽略该属性 不拷贝,忽略该属性
对基本类型null值处理(Integer,Long等) OK 特殊,会将null转为0,用Converter后解决 OK OK
对source特殊属性的限制:(Date,BigDecimal等) OK NO,异常出错,必须用Converter才行 OK OK
Get和set方法不匹配的处理 OK,忽略该属性 OK,忽略该属性 OK,忽略该属性 创建异常(仅限source有get方法在target中找不到对应set方法)
调用异常处理 需处理异常 需处理异常 RuntimeException RuntimeException

需要兼容性的场景,优先spring-beans提供的BeanUtils,功能强大,兼容性最好。需要性能的场景,优先选择cglib提供的BeanCopier,性能最好(注意需要缓存create结果)。

还有一项需要注意的是所有拷贝都是浅拷贝,注意属性引用不变,避免新老对象同时读写引起问题,这需要通过场景约束。

结论

事实上,由于不同工具类实现不同,细节差异非常多,一个团队最好通过规范统一使用某一个,并对其熟悉和理解,不必追求万能的工具类。我在新项目中选择了BeanCopier,并通过场景和限制约定避免踩坑。

  • 场景约束:主要用于同模型的DO、DTO和VO等之间转换。
  • 限制约定:要求两者同名属性具有严格相同类型,且其getter和setter一一对应,不能包含特殊逻辑。

猜你喜欢

转载自www.cnblogs.com/pyx0/p/copy-properties.html