⽬录
前⾔
问题的排查问题的扩展
前⾔
呵呵 前端时间使⽤ 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 // 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 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 呢?, 还是说 相关规范就是这么约定的呢 ?以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。 因篇幅问题不能全部显示,请点此查看更多更全内容