最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中BeanUtils.copyProperties的11個(gè)坑總結(jié)

 更新時(shí)間:2023年05月29日 09:25:35   作者:撿田螺的小男孩  
我們?nèi)粘i_(kāi)發(fā)中,經(jīng)常涉及到DO、DTO、VO對(duì)象屬性拷貝賦值,很容易想到org.springframework.beans.BeanUtils的copyProperties,它會(huì)自動(dòng)通過(guò)反射機(jī)制獲取源對(duì)象和目標(biāo)對(duì)象的屬性,pyProperties,會(huì)有好幾個(gè)坑呢,本文將給大家總結(jié)一下遇到的坑,需要的朋友可以參考下

第1個(gè)坑: 類型不匹配

@Data
public class SourceBean {
    private Long age;
}
@Data
public class TargetBean {
    private String age;
}
public class Test {
    public static void main(String[] args) {
        SourceBean source = new SourceBean();
        source.setAge(25L);
        TargetBean target = new TargetBean();
        BeanUtils.copyProperties(source, target);
        System.out.println(target.getAge());  //拷貝賦值失敗,輸出null
    }
}

在上述demo中,源對(duì)象SourceBeanage屬性是一個(gè)Long類型,而目標(biāo)對(duì)象TargetBeanage屬性是一個(gè)String類型。由于類型不匹配,BeanUtils.copyProperties不會(huì)賦值成功的。我跑demo的結(jié)果,控制臺(tái)輸出null。

第2個(gè)坑: BeanUtils.copyProperties是淺拷貝

先給大家復(fù)習(xí)一下,什么是深拷貝?什么是淺拷貝?

  • 淺拷貝是指創(chuàng)建一個(gè)新對(duì)象,該對(duì)象的屬性值與原始對(duì)象相同,但對(duì)于引用類型的屬性,仍然共享相同的引用。換句話說(shuō),淺拷貝只復(fù)制對(duì)象及其引用,而不復(fù)制引用指向的對(duì)象本身。
  • 深拷貝是指創(chuàng)建一個(gè)新對(duì)象,該對(duì)象的屬性值與原始對(duì)象相同,包括引用類型的屬性。深拷貝會(huì)遞歸復(fù)制引用對(duì)象,創(chuàng)建全新的對(duì)象,以確??截惡蟮膶?duì)象與原始對(duì)象完全獨(dú)立

我再給個(gè)代碼demo給大家看看哈:

public class Address {
    private String city;
    //getter 和 setter 方法省略
}
public class Person {
    private String name;
    private Address address;
    //getter 和 setter 方法省略
}
 Person sourcePerson = new Person();
 sourcePerson.setName("John");
 Address address = new Address();
 address.setCity("New York");
 sourcePerson.setAddress(address);
 Person targetPerson = new Person();
 BeanUtils.copyProperties(sourcePerson, targetPerson);
 sourcePerson.getAddress().setCity("London");
 System.out.println(targetPerson.getAddress().getCity());  // 輸出為 "London"

在上述示例中,源對(duì)象Person的屬性address是一個(gè)引用類型。當(dāng)使用BeanUtils.copyProperties方法進(jìn)行屬性復(fù)制時(shí),實(shí)際上只復(fù)制了引用,即目標(biāo)對(duì)象targetPersonaddress 屬性引用和源對(duì)象 sourcePersonaddress 屬性引用指向同一個(gè)對(duì)象。因此,當(dāng)修改源對(duì)象的address對(duì)象時(shí),目標(biāo)對(duì)象的address對(duì)象也會(huì)被修改。

大家日常開(kāi)發(fā)中,要注意這個(gè)坑哈~

第3個(gè)坑:屬性名稱不一致

 public class SourceBean {
    private String username;
    // getter 和 setter 方法省略
}
public class TargetBean {
    private String userName;
    // getter 和 setter 方法省略
}
 SourceBean source = new SourceBean();
 source.setUsername("撿田螺的小男孩");
 TargetBean target = new TargetBean();
 BeanUtils.copyProperties(source, target);
 System.out.println(target.getUserName());   // 輸出為 null

在上述示例中,源對(duì)象SourceBean 的屬性名稱是username,而目標(biāo)對(duì)象TargetBean的屬性名稱也是userName。但是,兩個(gè) username,一個(gè)N是大寫,一個(gè)n是小寫,即屬性名稱不一致,BeanUtils.copyProperties方法無(wú)法自動(dòng)映射這些屬性(無(wú)法忽略大小寫自動(dòng)匹配),因此目標(biāo)對(duì)象的userName屬性值為null。

大家日常開(kāi)發(fā)中,要注意這個(gè)坑哈~ 比如大小寫不一致,差一兩個(gè)字母等等。

第4個(gè)坑:Null 值覆蓋

@Data
public class SourceBean {
    private String name;
    private String address;
}
@Data
public class TargetBean {
    private String name;
    private String address;
}
SourceBean source = new SourceBean();
source.setName("John");
source.setAddress(null);
TargetBean target = new TargetBean();
target.setAddress("田螺address");
BeanUtils.copyProperties(source, target);
System.out.println(target.getAddress());  // 輸出為 null

在上述示例中,源對(duì)象 SourceBean 的 address 屬性值為 null。默認(rèn)情況下,BeanUtils.copyProperties 方法會(huì)將源對(duì)象中的 null 值屬性覆蓋到目標(biāo)對(duì)象中。因此,目標(biāo)對(duì)象的 address 屬性值也為 null。

如果你不希望 null 值覆蓋目標(biāo)對(duì)象中的屬性,可以使用 BeanUtils.copyProperties 方法的重載方法,并傳入一個(gè)自定義的 ConvertUtilsBean 實(shí)例來(lái)進(jìn)行配置。

第5個(gè)坑:注意引入的包

BeanUtils.copyProperties其實(shí)有兩個(gè)包,分別是spring、apache。大家注意一下哈,這兩個(gè)包,是有點(diǎn)不一樣的:

//org.springframework.beans.BeanUtils(源對(duì)象在左邊,目標(biāo)對(duì)象在右邊)
public static void copyProperties(Object source, Object target) throws BeansException 
//org.apache.commons.beanutils.BeanUtils(源對(duì)象在右邊,目標(biāo)對(duì)象在左邊)
public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException

大家使用的時(shí)候,要注意一下哈,千萬(wàn)注意自己引入的哪個(gè)BeanUtils,寫對(duì)應(yīng)參數(shù)位置。

第6個(gè)坑:Boolean類型數(shù)據(jù)+is屬性開(kāi)頭的坑

SourceBean和TargetBean中的都有個(gè)屬性isTianLuo,它們的數(shù)據(jù)類型保持不變,但是一個(gè)為基本類型boolean,一個(gè)為包裝類型Boolean

@Data
public class SourceBean {
    private boolean isTianLuo;
}
@Data
public class TargetBean {
    private Boolean isTianLuo;
}

跑測(cè)試用里的時(shí)候,發(fā)現(xiàn)賦值不上:

SourceBean source = new SourceBean();
source.setTianLuo(true);
TargetBean target = new TargetBean();
BeanUtils.copyProperties(source, target);
System.out.println(target.getIsTianLuo()); // 輸出為 null

為什么呢?即使是一個(gè)包裝類型,一個(gè)基本類型,應(yīng)該可以賦值上才對(duì)的。

這是因?yàn)楫?dāng)屬性類型為boolean時(shí),屬性名以is開(kāi)頭,屬性名會(huì)去掉前面的is,因此源對(duì)象和目標(biāo)對(duì)象屬性對(duì)不上啦。

大家使用BeanUtils.copyProperties過(guò)程中,要注意哈~

第7個(gè)坑:查找不到字段引用

在某些開(kāi)發(fā)場(chǎng)景呢,如果我們要修改某個(gè)字段的賦值,我們可能會(huì)全文搜索它的所有set方法,看哪些地方引用到。

但是呢,如果使用BeanUtils.copyProperties,就不知道是否引用到對(duì)應(yīng)的ste方法啦,即查找不到字段引用。這就可能導(dǎo)致你會(huì)漏掉修改對(duì)應(yīng)的字段。

第8個(gè)坑:不同內(nèi)部類,即使相同屬性,也是賦值失敗

@Data
public class CopySource {
    public String outerName;
    public CopySource.InnerClass innerClass;
    @Data
    public static class InnerClass {
        public String InnerName;
    }
}
@Data
public class CopyTarget {
    public String outerName;
    public CopyTarget.InnerClass innerClass;
    @Data
   public static class InnerClass {
        public String InnerName;
    }
}
CopySource test1 = new CopySource();
test1.outerName = "outTianluo";
CopySource.InnerClass innerClass = new CopySource.InnerClass();
innerClass.InnerName = "innerTianLuo";
test1.innerClass = innerClass;
System.out.println(test1);
CopyTarget test2 = new CopyTarget();
BeanUtils.copyProperties(test1, test2);
System.out.println(test2);  //輸出CopyTarget(outerName=outTianluo, innerClass=null)

以上demo中,CopySourceCopyTarget各自存在一個(gè)內(nèi)部類InnerClass,雖然這個(gè)內(nèi)部類屬性也相同,類名也相同,但是在不同的類中,因此Spring會(huì)認(rèn)為屬性不同,不會(huì)Copy;

如果要復(fù)制成功,可以讓他們指向同一個(gè)內(nèi)部類。

第9個(gè)坑:bean對(duì)應(yīng)的屬性,沒(méi)有g(shù)etter和setter方法,賦值失敗

BeanUtils.copyProperties要拷貝屬性值成功,需要對(duì)應(yīng)的bean要有getter和setter方法。因?yàn)樗怯梅瓷淠玫絪et和get方法再去拿屬性值和設(shè)置屬性值的。

@Data
public class SourceBean {
    private String value;
}
@Getter   //沒(méi)有對(duì)應(yīng)的setter方法
public class TargetBean {
    private String value;
}
SourceBean source = new SourceBean();
source.setValue("撿田螺的小男孩");
TargetBean target = new TargetBean();
BeanUtils.copyProperties(source, target);
System.out.println(target.getValue()); //輸出null 

第10個(gè)坑:BeanUtils.copyProperties + 泛型

如果BeanUtils.copyProperties遇到泛型,也是很可能賦值失敗的哈。大家看下這個(gè)例子:

@Data
public class CopySource {
    public String outerName;
    public List<CopySource.InnerClass> clazz;
    @Data
    public static class InnerClass {
        public String InnerName;
    }
}
@ToString
@Data
public class CopyTarget {
    public String outerName;
    public List<CopyTarget.InnerClass> clazz;
    @Data
    public static class InnerClass {
        public String InnerName;
    }
}
CopySource test1 = new CopySource();
test1.outerName = "outTianluo";
CopySource.InnerClass innerClass = new CopySource.InnerClass();
innerClass.InnerName = "innerTianLuo";
List<CopySource.InnerClass> clazz = new ArrayList<>();
clazz.add(innerClass);
test1.setClazz(clazz);
System.out.println(test1);
CopyTarget test2 = new CopyTarget();
BeanUtils.copyProperties(test1, test2);
System.out.println(test2);  //輸出CopyTarget(outerName=outTianluo, clazz=null)

這里面的例子,BeanUtils.copyProperties方法拷貝包含泛型屬性的對(duì)象clazzCopyTargetCopySource的泛型屬性類型不匹配,因此拷貝賦值失敗。

如果是低版本的包,泛型如果不匹配,則會(huì)報(bào)錯(cuò),高本版則知識(shí)拷貝賦值失敗。

第11個(gè)坑:性能問(wèn)題

由于這些BeanUtils類都是采用反射機(jī)制實(shí)現(xiàn)的,對(duì)程序的效率也會(huì)有影響。我跑了個(gè)demo對(duì)比:

SourceBean sourceBean = new SourceBean();
sourceBean.setName("tianLuoBoy");
TargetBean target = new TargetBean();
long beginTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {  //循環(huán)10萬(wàn)次
      target.setName(sourceBean.getName());
}
System.out.println("common setter time:" + (System.currentTimeMillis() - beginTime));
long beginTime1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {  //循環(huán)10萬(wàn)次
    BeanUtils.copyProperties(sourceBean, target);
}
System.out.println("bean copy time:" + (System.currentTimeMillis() - beginTime1));
//輸出
common setter time:3
bean copy time:331

可以發(fā)現(xiàn),簡(jiǎn)單的setterBeanUtils.copyProperties對(duì)比,性能差距非常大。因此,慎用BeanUtils.copyProperties?。。?/code>

12. 替換BeanUtils.copyProperties的方案

以上聊了BeanUtils.copyProperties的11個(gè)坑,都是在跟大家聊,要慎用BeanUtils.copyProperties。那有沒(méi)有推薦替換它的方案呢。

第一種,那就是使用原始的setter和getter方法。

使用手動(dòng)的setter方法進(jìn)行屬性賦值。這種方法可能需要編寫更多的代碼,但是可以提供更細(xì)粒度的控制,并且在性能方面通常比BeanUtils.copyProperties更高效。

Target target = new Target();
target.setName(source.getName());
target.setAge(source.getAge());

如果實(shí)在對(duì)象bean的屬性比較多的話,可以使用插件GenerateAllSetter,它可以一鍵生成對(duì)象的set方法,挺方便的。

第二種方案,使用映射工具庫(kù),如MapStruct、ModelMapper等,它們可以自動(dòng)生成屬性映射的代碼。這些工具庫(kù)可以減少手動(dòng)編寫setter方法的工作量,并提供更好的性能。

使用MapStruct的示例:

@Mapper
public interface SourceTargetMapper {
    SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
    @Mapping(source = "name", target = "name")
    @Mapping(source = "age", target = "age")
    Target mapToTarget(Source source);
}
Target target = SourceTargetMapper.INSTANCE.mapToTarget(source);

以上就是Java中BeanUtils.copyProperties容易遇到的11個(gè)坑總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Java BeanUtils.copyProperties坑的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 微信公眾平臺(tái)(測(cè)試接口)準(zhǔn)備工作

    微信公眾平臺(tái)(測(cè)試接口)準(zhǔn)備工作

    想要微信開(kāi)發(fā),首先要有個(gè)服務(wù)器,但是自己沒(méi)有。這時(shí)候可以用花生殼,將內(nèi)網(wǎng)映射到公網(wǎng)上,這樣就可以在公網(wǎng)訪問(wèn)自己的網(wǎng)站了。
    2016-05-05
  • Spring控制反轉(zhuǎn)和依賴注入超詳細(xì)講解

    Spring控制反轉(zhuǎn)和依賴注入超詳細(xì)講解

    控制反轉(zhuǎn)(IoC)與依賴注入(DI)是密切相關(guān)的概念,它們通常一起出現(xiàn)在討論Spring框架時(shí),這篇文章主要介紹了Spring控制反轉(zhuǎn)和依賴注入的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-10-10
  • SSL證書部署+SpringBoot實(shí)現(xiàn)HTTPS安全訪問(wèn)的操作方法

    SSL證書部署+SpringBoot實(shí)現(xiàn)HTTPS安全訪問(wèn)的操作方法

    文章介紹了SSL和HTTPS的工作原理,包括握手階段和安全數(shù)據(jù)傳輸階段,通過(guò)模擬HTTPS請(qǐng)求,展示了如何生成自簽名證書并配置Spring Boot應(yīng)用程序以支持HTTPS,總結(jié)指出,SSL和HTTPS對(duì)于保護(hù)網(wǎng)絡(luò)安全至關(guān)重要,感興趣的朋友一起看看吧
    2025-02-02
  • Java?spring?通過(guò)注解方式創(chuàng)建對(duì)象的示例詳解

    Java?spring?通過(guò)注解方式創(chuàng)建對(duì)象的示例詳解

    這篇文章主要介紹了java?spring?通過(guò)注解方式創(chuàng)建對(duì)象,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • Spring中BeanFactoryPostProcessors是如何執(zhí)行的

    Spring中BeanFactoryPostProcessors是如何執(zhí)行的

    BeanFactoryPostProcessor是Spring容器提供的一個(gè)擴(kuò)展機(jī)制,它允許開(kāi)發(fā)者在Bean的實(shí)例化和初始化之前對(duì)BeanDefinition進(jìn)行修改和處理,這篇文章主要介紹了你知道Spring中BeanFactoryPostProcessors是如何執(zhí)行的嗎,需要的朋友可以參考下
    2023-11-11
  • 如何使用IDEA開(kāi)發(fā)Spark SQL程序(一文搞懂)

    如何使用IDEA開(kāi)發(fā)Spark SQL程序(一文搞懂)

    Spark SQL 是一個(gè)用來(lái)處理結(jié)構(gòu)化數(shù)據(jù)的spark組件。它提供了一個(gè)叫做DataFrames的可編程抽象數(shù)據(jù)模型,并且可被視為一個(gè)分布式的SQL查詢引擎。這篇文章主要介紹了如何使用IDEA開(kāi)發(fā)Spark SQL程序(一文搞懂),需要的朋友可以參考下
    2021-08-08
  • Java程序?qū)崿F(xiàn)連接數(shù)據(jù)庫(kù)

    Java程序?qū)崿F(xiàn)連接數(shù)據(jù)庫(kù)

    本文詳細(xì)介紹了在Java程序中導(dǎo)入jar包、連接數(shù)據(jù)庫(kù)并操作表格的方法,包括新建數(shù)據(jù)庫(kù)、創(chuàng)建表、編寫Java代碼連接數(shù)據(jù)庫(kù)等步驟,最后通過(guò)返回結(jié)果集輸出數(shù)據(jù)
    2026-04-04
  • java中catalina.home與catalina.base區(qū)別點(diǎn)整理

    java中catalina.home與catalina.base區(qū)別點(diǎn)整理

    在本篇文章里小編給大家整理的是關(guān)于java項(xiàng)目中catalina.home與catalina.base區(qū)別點(diǎn),需要的朋友們可以學(xué)習(xí)下。
    2020-02-02
  • 使用Zookeeper實(shí)現(xiàn)分布式鎖

    使用Zookeeper實(shí)現(xiàn)分布式鎖

    這篇文章主要介紹了使用Zookeeper實(shí)現(xiàn)分布式鎖,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • java非公平鎖知識(shí)點(diǎn)實(shí)例詳解

    java非公平鎖知識(shí)點(diǎn)實(shí)例詳解

    在本篇文章里小編給大家整理了一篇關(guān)于java非公平鎖知識(shí)點(diǎn)實(shí)例詳解,有興趣的朋友們可以學(xué)習(xí)參考下。
    2021-10-10

最新評(píng)論

丽江市| 海伦市| 扎赉特旗| 平顶山市| 马公市| 南乐县| 商水县| 东乡族自治县| 通道| 东至县| 丰城市| 抚州市| 永修县| 无极县| 章丘市| 赣榆县| 大连市| 固原市| 南安市| 故城县| 洛浦县| 德化县| 丹东市| 沾益县| 敦化市| 云南省| 宁蒗| 盐城市| 华池县| 张家界市| 宜城市| 东乌| 法库县| 卢氏县| 大邑县| 揭东县| 新河县| 望奎县| 高邮市| 姜堰市| 拜泉县|