Spring中BeanUtils.copyProperties的隐藏陷阱

hello大家好,相信大家对这个工具类的使用一定很频繁吧,特别是在写接口的时候进行拷贝DTO对象的时候。最近写项目的时候发现这个spring中这个BeanUtils.copyProperties()中的一些小坑。这里想分享给分大家。下面我写了一个案例大家可以参考一下。

Person类,并且使用了依赖lombook.

Stundent类,并且使用了依赖lombook.

可以看到上面两个类中拥有同样的属性name,并且两者的数据类型是一致的,但是sex属性在Person和Student中的数据类型不一致,这里我们要验证如果数据类型不一致的话是否可以将Person里面的sex属性拷贝给Student属性呢。话不多说,直接测试。可以发现下面的这个实际上是并没有进行拷贝成功的。我查了一下这个实际上底层用的是发射技术实现的拷贝,

1.首先会获取source对象的getter()方法,也就是这里的Person对象的getSex()

2.然后获取到Target对象的setter()方法,也就是这里的Student对象的setSex()

3.然后将掉用source对象的getSex()方法作为参数传递给目标对象的setSex(String sex)方法,但是不要忽略了一点源对象的返回值是char类型,但是目标对象的参数是String类型,因此是不会进行拷贝的。这个工具会认为这两个属性并不相同。实际上也确实不相同,因为数据类型不相同,如何进行拷贝呢。因此sex属性拷贝失败,反观name属性,属性名和数据类型都一样,确实也注入成功了。

还有一个很好玩的点是:

如果name的属性名两者并不一样的话,那么还能拷贝成功吗。直接上代码。 

这里我修改了Person里面的属性名。而Student的属性名不变。

经过测试发现,其实也是拷贝不成功的。

这就应证了是通过反射技术来查看得到两者其实不是一个同一个变量,具体的流程就是查看源对象的getNAme(),然后推断出属性名是nAme,返回类型是String.紧接着查看目标对象setName(String name)方法,得到属性为name,参数为String ,最终判断两者的属性名并不相同。无法拷贝。

还有一种特殊的情况就是把源对象的name属性改为Name,而目标对象仍然为name,这样也是可以拷贝成功的。因为是通过方法名进行推断的两者的属性名是否相同,而Javabean类的setter和getter默认都是驼峰命名的,因此是可以进行拷贝的,大家也可以试试哦。感谢大家观看,有歧义的地方在请及时指出。

### Spring Framework 中 `BeanUtils.copyProperties` 方法的拷贝方式 #### 浅拷贝与深拷贝的区别 浅拷贝和深拷贝的主要区别在于对象引用的处理方式。 - **浅拷贝**仅复制对象中的基本数据类型以及引用类型的地址,而不创建新的对象实例。这意味着源对象和目标对象共享相同的子对象引用[^4]。 - **深拷贝**不仅复制对象本身,还会递归地复制所有的子对象,从而确保源对象和目标对象完全独立。 #### `BeanUtils.copyProperties` 的实现原理 `org.springframework.beans.BeanUtils.copyProperties()` 是基于 Java 反射机制实现的一种浅拷贝方法[^1]。它主要用于将一个对象的属性值复制到另一个对象中。具体来说: - 它会遍历源对象的所有可读属性(即具有 getter 方法的属性),并将这些属性的值设置到目标对象对应的可写属性(即具有 setter 方法的属性)上。 - 如果两个对象之间存在同名且兼容类型的属性,则它们会被自动映射并赋值。 - 对于布尔类型字段,其 readMethod 需要遵循特定规则,例如以 `getXXX` 而不是 `isXXX` 来命名[^2]。 需要注意的是,该方法不会递归地复制嵌套对象的内容,而是简单地将源对象中引用的对象地址赋值给目标对象。因此,这属于一种典型的浅拷贝行为。 #### 示例代码展示 以下是使用 `BeanUtils.copyProperties` 进行浅拷贝的一个示例: ```java import org.springframework.beans.BeanUtils; public class CopyExample { public static void main(String[] args) { Source source = new Source(); source.setId(1); source.setName("Test"); Target target = new Target(); BeanUtils.copyProperties(source, target); System.out.println(target.getId()); // 输出 1 System.out.println(target.getName()); // 输出 Test } } class Source { private int id; private String name; // Getters and Setters public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } class Target { private int id; private String name; // Getters and Setters public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` 在这个例子中,`Source` 和 `Target` 类之间的属性被成功复制,但由于这是浅拷贝,任何复杂的嵌套对象都不会被真正克隆。 #### 使用注意事项 由于 `BeanUtils.copyProperties` 实现的是浅拷贝,在某些场景下可能会引发问题。例如,当源对象包含复杂的数据结构(如列表、集合或其他自定义类)时,修改其中一个对象可能会影响另一个对象的行为。为了避免这种情况,开发者通常需要自行实现深拷贝逻辑或借助第三方库完成更深层次的复制操作。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值