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 <[email protected]> * @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 <[email protected]> * @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 <[email protected]> * @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<String> getAttr() { return Optional.of(attr); } // public String getAttr() { // return attr; // } public void setAttr(String attr) { this.attr = attr; } }
以上測試代碼輸出結果為 :
從測試代碼中可以看到這裡有兩個 BeanUtils.copyProperties 的使用, 並且兩個都沒有拷貝成功, 我們一個一個的來看
首先是第一個 BeanUtils.copyProperties, 來源對象 和 目標對象分別為 ImmutableEntity 和 MutableEntity
ImmutableEntity 上面有 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 <[email protected]> * @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 <[email protected]> * @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 <[email protected]> * @version 1.0 * @date 2020-02-24 12:54 */ class MutableEntity { // attr private String attr; public MutableEntity(String attr) { this.attr = attr; } // public Optional<String> 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 呢?, 還是說 相關規范就是這麼約定的呢 ?
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- BeanUtils.copyProperties()拷貝id屬性失敗的原因及解決
- Java BeanUtils工具類常用方法講解
- 基於Spring BeanUtils的copyProperties方法使用及註意事項
- 聊聊BeanUtils.copyProperties和clone()方法的區別
- BeanUtils.copyProperties擴展–實現String轉Date