BeanUtils.copyProperties復(fù)制不生效的解決
前言
呵呵 前端時(shí)間使用 BeanUtils.copyProperties 的時(shí)候碰到了一個(gè)這樣的問(wèn)題
我有兩個(gè)實(shí)體, 有同樣的屬性, 一個(gè)有給定的屬性的 getter, 另外一個(gè)有 給定的屬性的 setter, 但是 我使用 BeanUtils.copyProperties 的時(shí)候 把來(lái)源對(duì)象的這個(gè)屬性 復(fù)制不到 目標(biāo)對(duì)象上面
然后 當(dāng)時(shí)也跟蹤了一下代碼, 然后 這里整理一下 改代碼片段吧
然后在調(diào)試的過(guò)程中 也發(fā)現(xiàn)了一些其他的問(wèn)題, 呵呵 算是額外的了解吧
一下代碼基于 : jdk1.8.0_211 + commons-beanutils 1.9.4
問(wèn)題的排查
首先來(lái)一段測(cè)試用例, 里面主要包含了三個(gè)類(lèi), 一個(gè)測(cè)試類(lèi), 兩個(gè)實(shí)體類(lèi)
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 對(duì)應(yīng)的類(lèi)型不匹配
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<String> getAttr() {
return Optional.of(attr);
}
// public String getAttr() {
// return attr;
// }
public void setAttr(String attr) {
this.attr = attr;
}
}
以上測(cè)試代碼輸出結(jié)果為 :

從測(cè)試代碼中可以看到這里有兩個(gè) BeanUtils.copyProperties 的使用, 并且兩個(gè)都沒(méi)有拷貝成功, 我們一個(gè)一個(gè)的來(lái)看
首先是第一個(gè) BeanUtils.copyProperties, 來(lái)源對(duì)象 和 目標(biāo)對(duì)象分別為 ImmutableEntity 和 MutableEntity
ImmutableEntity 上面有 getAttr, MutableEntity 上面有 setAttr, 但是為什么沒(méi)有拷貝成功呢 ?
在下圖的地方打一個(gè)斷點(diǎn) 調(diào)試一下

調(diào)試發(fā)現(xiàn) 源對(duì)象是可讀的, 但是 目標(biāo)對(duì)象不可寫(xiě)?, 為什么呢?, 我們的 MutableEntity 不是有 setAttr 么

在 processPropertyDescriptor 方法之后, 我們發(fā)現(xiàn) attr 屬性, 居然不可寫(xiě)了 ?
具體到 processPropertyDescriptor 方法, 他主要干的事情是
// 1. 尋找 getter(存在多個(gè)merge) // First pass. Find the latest getter method. Merge properties // of previous getter methods. // 2. 尋找 setter(存在多個(gè)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.
以上注釋來(lái)自于 Introspector.java, 1, 2, 3 的注釋來(lái)自于我
我們這里重點(diǎn)關(guān)注 step2, 需要找到 類(lèi)型匹配 getter 類(lèi)型的 setter 方法, 但是我們這里的情況是 getter 返回值是 Optional, setter 返回值是 String, 因此類(lèi)型不匹配 所以我們上面看到的結(jié)果是 有 getter, 沒(méi)得 setter
實(shí)際的上下文信息如下圖

以上便是 第一個(gè) BeanUtils.copyProperties 不生效的原因了
第二個(gè) BeanUtils.copyProperties, 原因也是同上, 不過(guò)直觀(guān)的理解來(lái)說(shuō), attr 是有 getter 并且有 setter 的, 但是 由于規(guī)范的約定, 因此 propertyDescriptor 里面有 getter, 沒(méi)得 setter
問(wèn)題的擴(kuò)展
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 對(duì)應(yīng)的類(lèi)型不匹配
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<String> getAttr() {
// return Optional.of(attr);
// }
public String getAttr() {
return attr;
}
public void setAttr(String attr) {
this.attr = attr;
}
}
我們吧如上代碼 整理到同一個(gè)文件中(這其實(shí)才是第一個(gè) demo, 上文中的是第二個(gè) demo), 并且調(diào)整了 MutableEntity.getter 使其和 setter 的類(lèi)型能夠匹配
但是我們一跑, 發(fā)現(xiàn)結(jié)果還是有些出人意料

BeanUtilsBean 如下地方打一個(gè)斷點(diǎn)

我們發(fā)現(xiàn)這里有一個(gè)奇怪的現(xiàn)象, 源對(duì)象不可讀, 目標(biāo)對(duì)象不可寫(xiě)??, 這是怎么回事 ?
以 ImmutableEntity. getAttr 為例, 我們?cè)?MethodUtils.getAccessableMethod 里面如下地方打一個(gè)斷點(diǎn)

我們發(fā)現(xiàn) 尋找目標(biāo)的方法主要有圖中 三個(gè)地方
第一個(gè)是當(dāng)前類(lèi), 另外一個(gè)是當(dāng)前類(lèi)實(shí)現(xiàn)的接口, 另外一個(gè)是 當(dāng)前類(lèi)的基類(lèi)(上圖還有未截取完的一部分, 限定 method 必須為 public, 否則不允許訪(fǎng)問(wèn))
- 1. 在當(dāng)前類(lèi)查詢(xún) : 首先需要限定當(dāng)前類(lèi)是 public(我們這里不滿(mǎn)足) public 允許訪(fǎng)問(wèn)
- 2. 當(dāng)前類(lèi)實(shí)現(xiàn)的接口查詢(xún) : 獲取接口以及父接口中 匹配方法名字, 參數(shù)列表 的方法
- 3. 當(dāng)前類(lèi)的基類(lèi)查詢(xún) : 獲取基類(lèi)以及更上的基類(lèi)中, 并且是 public 的基類(lèi), 匹配方法名字, 參數(shù)列表 的方法
因此, 我們這里的 第二個(gè)例子的 兩個(gè) BeanUtils.copyProperties 也沒(méi)有生效
呵呵 不知道這個(gè)限定類(lèi)為 public 的限定是否是 bug 呢?, 還是說(shuō) 相關(guān)規(guī)范就是這么約定的呢 ?
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot2中session超時(shí),退到登錄頁(yè)面方式
這篇文章主要介紹了springboot2中session超時(shí),退到登錄頁(yè)面方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
mybatis?plus實(shí)現(xiàn)分頁(yè)邏輯刪除
這篇文章主要為大家介紹了mybatis?plus實(shí)現(xiàn)分頁(yè)邏輯刪除的方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
SpringBoot啟動(dòng)器Starters使用及原理解析
這篇文章主要介紹了SpringBoot啟動(dòng)器Starters使用及原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
jar包運(yùn)行一段時(shí)間后莫名其妙掛掉線(xiàn)上問(wèn)題及處理方案
這篇文章主要介紹了jar包運(yùn)行一段時(shí)間后莫名其妙掛掉線(xiàn)上問(wèn)題及處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
jtds1.1連接sqlserver2000測(cè)試示例
這篇文章主要介紹了jtds1.1連接sqlserver2000測(cè)試示例,需要的朋友可以參考下2014-02-02
Springboot項(xiàng)目刪除項(xiàng)目同步target文件問(wèn)題解決方案
這篇文章主要介紹了Springboot項(xiàng)目刪除項(xiàng)目同步target文件問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12

