您的当前位置:首页BeanUtils.copyProperties复制不生效的解决

BeanUtils.copyProperties复制不生效的解决

2020-04-08 来源:乌哈旅游
BeanUtils.copyProperties复制不⽣效的解决

⽬录

前⾔

问题的排查问题的扩展

前⾔

呵呵 前端时间使⽤ BeanUtils.copyProperties 的时候碰到了⼀个这样的问题

我有两个实体, 有同样的属性, ⼀个有给定的属性的 getter, 另外⼀个有 给定的属性的 setter, 但是 我使⽤ BeanUtils.copyProperties 的时候 把来源对象的这个属性 复制不到 ⽬标对象上⾯然后 当时也跟踪了⼀下代码, 然后 这⾥整理⼀下 改代码⽚段吧然后在调试的过程中 也发现了⼀些其他的问题, 呵呵 算是额外的了解吧⼀下代码基于 : jdk1.8.0_211 + commons-beanutils 1.9.4

问题的排查

⾸先来⼀段测试⽤例, ⾥⾯主要包含了三个类, ⼀个测试类, 两个实体类

package com.hx.test03;

import org.apache.commons.beanutils.BeanUtils; /**

* Test24BeanUtilsCopy *

* @author Jerry.X.He <970655147@qq.com> * @version 1.0

* @date 2020-02-25 16:55 */

public class Test24BeanUtilsCopy {

// Test24BeanUtilsCopy

// 1. 取的 source 的 propertyDescriptor // 2. get, set 对应的类型不匹配

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

Test24ImmutableEntity fromImmutable = new Test24ImmutableEntity(\"fromImmutable\"); Test24MutableEntity fromMutable = new Test24MutableEntity(\"fromMutable\"); Test24MutableEntity targetEntity = new Test24MutableEntity(\"targetEntity\");

// does't work

BeanUtils.copyProperties(targetEntity, fromImmutable); System.out.println(targetEntity.getAttr()); // does't work

BeanUtils.copyProperties(targetEntity, fromMutable); System.out.println(targetEntity.getAttr()); }}

package com.hx.test03; /**

* ImmutablePayment *

* @author Jerry.X.He <970655147@qq.com> * @version 1.0

* @date 2020-02-25 16:32 */

public class Test24ImmutableEntity {

// attr

private final String attr;

public Test24ImmutableEntity(String attr) { this.attr = attr; }

public String getAttr() { return attr; } }

package com.hx.test03; import java.util.Optional; /**

* ImmutablePayment *

* @author Jerry.X.He <970655147@qq.com> * @version 1.0

* @date 2020-02-25 16:32 */

public class Test24MutableEntity {

// attr

private String attr;

public Test24MutableEntity(String attr) { this.attr = attr; }

public Optional getAttr() { return Optional.of(attr); }

// public String getAttr() {// return attr;// }

public void setAttr(String attr) { this.attr = attr; } }

以上测试代码输出结果为 :

从测试代码中可以看到这⾥有两个 BeanUtils.copyProperties 的使⽤, 并且两个都没有拷贝成功, 我们⼀个⼀个的来看⾸先是第⼀个 BeanUtils.copyProperties, 来源对象 和 ⽬标对象分别为 ImmutableEntity 和 MutableEntityImmutableEntity 上⾯有 getAttr, MutableEntity 上⾯有 setAttr, 但是为什么没有拷贝成功呢 ?在下图的地⽅打⼀个断点 调试⼀下

调试发现 源对象是可读的, 但是 ⽬标对象不可写?, 为什么呢?, 我们的 MutableEntity 不是有 setAttr 么

在 processPropertyDescriptor ⽅法之后, 我们发现 attr 属性, 居然不可写了 ?具体到 processPropertyDescriptor ⽅法, 他主要⼲的事情是

// 1. 寻找 getter(存在多个merge)

// First pass. Find the latest getter method. Merge properties// of previous getter methods.

// 2. 寻找 setter(存在多个merge)

// Second pass. Find the latest setter method which// has the same type as the getter method.

// 3. merge getter & setter

// At this stage we should have either PDs or IPDs for the// representative getters and setters. The order at which the// property descriptors are determined represent the// precedence of the property ordering.

以上注释来⾃于 Introspector.java, 1, 2, 3 的注释来⾃于我

我们这⾥重点关注 step2, 需要找到 类型匹配 getter 类型的 setter ⽅法, 但是我们这⾥的情况是 getter 返回值是 Optional, setter 返回值是 String, 因此类型不匹配 所以我们上⾯看到的结果是有 getter, 没得 setter实际的上下⽂信息如下图

以上便是 第⼀个 BeanUtils.copyProperties 不⽣效的原因了

第⼆个 BeanUtils.copyProperties, 原因也是同上, 不过直观的理解来说, attr 是有 getter 并且有 setter 的, 但是 由于规范的约定, 因此 propertyDescriptor ⾥⾯有 getter, 没得 setter

问题的扩展

package com.hx.test03;

import org.apache.commons.beanutils.BeanUtils; /**

* BeanUtilsCopy *

* @author Jerry.X.He <970655147@qq.com> * @version 1.0

* @date 2020-02-24 12:49 */

public class Test23BeanUtilsCopy {

// Test23BeanUtilsCopy

// 1. 取的 source 的 propertyDescriptor // 2. get, set 对应的类型不匹配

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

ImmutableEntity fromImmutable = new ImmutableEntity(\"fromImmutable\"); MutableEntity fromMutable = new MutableEntity(\"fromMutable\"); MutableEntity targetEntity = new MutableEntity(\"targetEntity\");

// does't work

BeanUtils.copyProperties(targetEntity, fromImmutable); System.out.println(targetEntity.getAttr()); // does't work

BeanUtils.copyProperties(targetEntity, fromMutable); System.out.println(targetEntity.getAttr()); }} /**

* ImmutablePayment *

* @author Jerry.X.He <970655147@qq.com> * @version 1.0

* @date 2020-02-24 12:50 */

class ImmutableEntity { // attr

private final String attr;

public ImmutableEntity(String attr) { this.attr = attr; }

public String getAttr() { return attr; }} /**

* MutablePayment *

* @author Jerry.X.He <970655147@qq.com> * @version 1.0

* @date 2020-02-24 12:54 */

class MutableEntity { // attr

private String attr;

public MutableEntity(String attr) {

this.attr = attr; }

// public Optional getAttr() {// return Optional.of(attr);// }

public String getAttr() { return attr; }

public void setAttr(String attr) { this.attr = attr; }}

我们吧如上代码 整理到同⼀个⽂件中(这其实才是第⼀个 demo, 上⽂中的是第⼆个 demo), 并且调整了 MutableEntity.getter 使其和 setter 的类型能够匹配但是我们⼀跑, 发现结果还是有些出⼈意料

BeanUtilsBean 如下地⽅打⼀个断点

我们发现这⾥有⼀个奇怪的现象, 源对象不可读, ⽬标对象不可写??, 这是怎么回事 ?

以 ImmutableEntity. getAttr 为例, 我们在 MethodUtils.getAccessableMethod ⾥⾯如下地⽅打⼀个断点

我们发现 寻找⽬标的⽅法主要有图中 三个地⽅

第⼀个是当前类, 另外⼀个是当前类实现的接⼝, 另外⼀个是 当前类的基类(上图还有未截取完的⼀部分, 限定 method 必须为 public, 否则不允许访问)

1. 在当前类查询 : ⾸先需要限定当前类是 public(我们这⾥不满⾜) public 允许访问2. 当前类实现的接⼝查询 : 获取接⼝以及⽗接⼝中 匹配⽅法名字, 参数列表 的⽅法

3. 当前类的基类查询 : 获取基类以及更上的基类中, 并且是 public 的基类, 匹配⽅法名字, 参数列表 的⽅法因此, 我们这⾥的 第⼆个例⼦的 两个 BeanUtils.copyProperties 也没有⽣效

呵呵 不知道这个限定类为 public 的限定是否是 bug 呢?, 还是说 相关规范就是这么约定的呢 ?以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。

因篇幅问题不能全部显示,请点此查看更多更全内容