前言:
  BeanUtils(spring版/apache版)工具极大方便了java developer, 尤其在写业务代码中, 各种域模型DO, BO, VO等对象之间的复制. 但使用BeanUtils过程中, 也有些细节需要注意, 避免遇到一些神坑. 比如使用BeanUtils时最容易犯的错, 复制对象采用的是浅拷贝模式, 而并非预想的深拷贝模式.
  本文将讲解BeanUtils在遇到泛型时, 需要注意的一些问题.

复制特点:
  BeanUtils在复制(copyProperties)对象过程中, 除了开头提到过的浅拷贝模式外, 还具有以下一些特点.
  1. 成员存在性不一致
  source对象有, 但是dest对象没有, 这些成员属性直接忽略
  source对象没有, 但是dest对象有, 则dest的成员属性选用默认值.
  2. 名称和类型强匹配
  只有当source和dest的成员, 其名称和类型完全匹配时, 才进行复制. 唯一的例外, 是String和Date类型, BeanUtils有默认内置的Convertor允许互转(比较特殊).

场景模拟:
  让我们回到主题, 既然谈到了泛型, 那么我们来模拟一个案例, 来看看到底发生了什么?

@Setter
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
class Req<T> {
String name;
T value;
} @Setter
@Getter
@AllArgsConstructor
class Hello {
String key;
} @Setter
@Getter
@AllArgsConstructor
class World {
String key;
} public class TestCase { @Test
public void test() {
// t1为源对象
Req<Hello> t1 = new Req<Hello>("lilei", new Hello("key"));
// t2为目标对象
Req<World> t2 = new Req<World>(); // 借助spring的BeanUtils来复制对象, source->dest
BeanUtils.copyProperties(t1, t2); // 打印t2对象的内容
System.out.println(t2); // t2的value值预期为null
Assert.assertEquals(t2.getValue(), null);
} }

  执行的结果如下所示:

Req(name=lilei, value=com.test.Hello@4c178a76)

java.lang.AssertionError:
Expected :com.test.Hello@4c178a76
Actual :null

  和预期完全相反, 从打印对象t2中, 我们惊奇的发现, t2(类型为Req<World>)对象的成员value(World类型)竟然变成了Hello类型. 当使用t2对象的value成员时, 会在运行期遇到cast class的异常, 非常的诡异.
  不是说好, BeanUtils在复制对象时, 严格执行名称和类型强匹配的原则吗? 这是光天化日之下的打脸, ^_^.

分析和解决:
  一方面, 这个现象应该和java泛型的特殊性有关系, java泛型在编译时存在, 但是在编译后的字节码中就不复存在了, 或者说其在运行期其泛型类型已被擦拭掉了. 因此Req<Hello>和Req<World>在运行期内被统一视为Req<Object>类型, 所以t1对象的Hello类型value被赋予给了t2对象World类型的value.
  另一方面, BeanUtils.copyProperties其是基于反射来实现对象成员的复制的, 因此回避掉了编译期的检查.
  综上所述, 上篇代码的执行结果就可以合理和解释了.
  那如何解决这个问题呢? 可以针对泛型成员单独复制来解决该问题.

public class TestCase {

    @Test
public void test() {
// t1为源对象
Req<Hello> t1 = new Req<Hello>("lilei", new Hello("key"));
// t2为目标对象
Req<World> t2 = new Req<World>(); // 借助spring的BeanUtils来复制对象, source->dest
BeanUtils.copyProperties(t1, t2);
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 既解决深拷贝问题, 又纠正泛型问题
if ( t1.getValue() != null ) {
t2.setValue(new World(""));
BeanUtils.copyProperties(t1.getValue(), t2.getValue());
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 打印t2对象的内容
System.out.println(t2); // t2的value值预期为null
Assert.assertEquals(t2.getValue().getKey(), "key");
} }

  注:  "++++++++++++++++++++++"串包围的代码段尝试去纠正了这个问题, 测试也可以.

  测试结果如下:

Req(name=lilei, value=com.test.World@fa4c865)

  

总结:
  这个问题场景, 也是实际开发中遇到, 也算是对java泛型和BeanUtils再次认识的一个很好的例子.

当BeanUtils遇到泛型的更多相关文章

  1. JavaBean 内省API BeanUtils工具 泛型 xml xml约束

    1 什么是JavaBean?有何特征? 1)符合特定规则的类    2)JavaBean分二类:     a)侠义的JavaBean         .私有的字段(Field)         .对私 ...

  2. 基于表单数据的封装,泛型,反射以及使用BeanUtils进行处理

    在Java Web开发过程中,会遇到很多的表单数据的提交和对表单数据的处理.而每次都需要对这些数据的字段进行一个一个的处理就显得尤为繁琐,在Java语言中,面向对象的存在目的便是为了消除重复代码,减少 ...

  3. 一个好用的hibernate泛型dao

    以前从springside2.0上搞下来的很好用的,基本实现dao零编码只要配置xml文件就行了. 先看图: 一共4层,com.demonstration.hibernate.basedao是我加的用 ...

  4. java 反射,注解,泛型,内省(高级知识点)

     Java反射 1.Java反射是Java被视为动态(或准动态)语言的一个关键性质.这个机制允许程序在运行时透过Reflection APIs    取得任何一个已知名称的class的内部信息, 包括 ...

  5. BeanUtils在web项目中的应用

    package cn.gdpe.jdbc; import java.util.Enumeration; import javax.servlet.http.HttpServletRequest; im ...

  6. BeanUtils制作自定义的转换器

    一般来说,BeanUtils自带的Converter基本上可以满足我们在开发过程中的使用了,然而很多时候我们还是需要自定义一些转换器. MyBean.java package beanutils; i ...

  7. Java基础---Java---基础加强---内省的简单运用、注解的定义与反射调用、 自定义注解及其应用、泛型及泛型的高级应用、泛型集合的综合

    内省的简单运用: JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则. 采用遍历BeanInfo的所有属性方式来查找和 ...

  8. Java下的框架编程(反射,泛型,元数据,CGLib,代码动态生成,AOP,动态语言嵌入)

    Java 虽然没有动态语言般暴起,但仍然天连天,水接水的生出好多框架技术---反射(reflection),泛型(generics),元数据(annotation),proxies(proxy/cgl ...

  9. BeanUtils简化数据封装

    BeanUtils主要用来封装JavaBean的. 1.什么是JavaBean JavaBean指的是标准的类. 要求: 1. 类必须被public修饰2. 必须提供空参的构造器3. 成员变量必须使用 ...

随机推荐

  1. Shiro集成web环境[Springboot]-基础使用

    Shiro集成web环境[Springboot] 1.shiro官网查找依赖的jar,其中shiro-ehcache做授权缓存时使用,另外还需要导入ehcache的jar包 <dependenc ...

  2. git 连接github的配置

    这段时间要先在git上开发,上传代码到github上,所以首先需配置本地的git和github. 这几篇文章都不错,可以参考一下,大体的配置都很清楚. 1:https://blog.csdn.net/ ...

  3. Segments

    Segments Given n segments in the two dimensional space, write a program, which determines if there e ...

  4. Units about ASM

    1.ASM Striping and Mirroring:ASM supports two levels of striping: fine striping and coarse striping. ...

  5. 自定义putty主题

    PuTTY很早之前就没有更新了(0.62),因为都是开源的所以有人branch出来做了增强,如这个PuTTY tray,增加了超链等功能: https://puttytray.goeswhere.co ...

  6. py request.post header

    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome ...

  7. idea中deBug方法

    1 2设置controller层断点鼠标左键点击,断点在哪里,就会deBug到哪里 3刷新页面 4查看 5service层设置断点 6 7查看返回信息是否错误

  8. zabbix_server.conf、zabbix_agentd.conf配置文件详解

    zabbix_server.conf配置文件详解 AlertScriptsPath 默认值:/usr/local/share/zabbix/alertscripts 说明:告警脚本目录 AllowRo ...

  9. win7下使用U盘安装双系统(Ubuntu-17)

    1.首先下载Ubuntu镜像文件,下载地址:http://mirrors.neusoft.edu.cn/ 2.下载 U盘操作系统安装工具- Universal USB Installer ,下载地址: ...

  10. HashMap与TreeMap按照key和value排序

    package com.sort; import java.util.ArrayList; import java.util.Collections; import java.util.Compara ...