偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

BeanUtils.copyProperties的11個坑

開發(fā) 前端
淺拷貝是指創(chuàng)建一個新對象,該對象的屬性值與原始對象相同,但對于引用類型的屬性,仍然共享相同的引用。換句話說,淺拷貝只復(fù)制對象及其引用,而不復(fù)制引用指向的對象本身。

前言

大家好,我是田螺。

我們?nèi)粘i_發(fā)中,經(jīng)常涉及到DO、DTO、VO對象屬性拷貝賦值,很容易想到org.springframework.beans.BeanUtils的copyProperties 。它會自動通過反射機(jī)制獲取源對象和目標(biāo)對象的屬性,并將對應(yīng)的屬性值進(jìn)行復(fù)制??梢詼p少手動編寫屬性復(fù)制代碼的工作量,提高代碼的可讀性和維護(hù)性。

但是你知道嘛?使用BeanUtils的copyProperties ,會有好幾個坑呢,今天田螺哥給大家盤點(diǎn)一下哈:

圖片

第1個坑:類型不匹配

@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中,源對象SourceBean的age屬性是一個Long類型,而目標(biāo)對象TargetBean的age屬性是一個String類型。由于類型不匹配,BeanUtils.copyProperties不會賦值成功的。我跑demo的結(jié)果,控制臺輸出null。

第2個坑: BeanUtils.copyProperties是淺拷貝

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

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

圖片

我再給個代碼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"

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

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

第3個坑:屬性名稱不一致

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

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

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

第4個坑: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

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

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

第5個坑:注意引入的包

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

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

大家使用的時候,要注意一下哈,注意自己引入的哪個BeanUtils,寫對應(yīng)參數(shù)位置。

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

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

@Data
public class SourceBean {
    private boolean isTianLuo;
}

@Data
public class TargetBean {
    private Boolean isTianLuo;
}

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

SourceBean source = new SourceBean();
source.setTianLuo(true);

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

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

這是因為當(dāng)屬性類型為boolean時,屬性名以is開頭,屬性名會去掉前面的is,因此源對象和目標(biāo)對象屬性對不上啦。

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

第7個坑:查找不到字段引用

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

圖片

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

圖片

第8個坑:不同內(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中,CopySource和CopyTarget各自存在一個內(nèi)部類InnerClass,雖然這個內(nèi)部類屬性也相同,類名也相同,但是在不同的類中,因此Spring會認(rèn)為屬性不同,不會Copy;

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

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

BeanUtils.copyProperties要拷貝屬性值成功,需要對應(yīng)的bean要有g(shù)etter和setter方法。因為它是用反射拿到set和get方法再去拿屬性值和設(shè)置屬性值的。

@Data
public class SourceBean {
    private String value;
}

@Getter   //沒有對應(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個坑:BeanUtils.copyProperties + 泛型

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

@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方法拷貝包含泛型屬性的對象clazz。CopyTarget和CopySource的泛型屬性類型不匹配,因此拷貝賦值失敗。

第11個坑:性能問題

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

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萬次
      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萬次
    BeanUtils.copyProperties(sourceBean, target);
}
System.out.println("bean copy time:" + (System.currentTimeMillis() - beginTime1));

//輸出
common setter time:3
bean copy time:331

可以發(fā)現(xiàn),簡單的setter和BeanUtils.copyProperties對比,性能差距非常大。因此,慎用BeanUtils.copyProperties?。?!

12. 替換BeanUtils.copyProperties的方案

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

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

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

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

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

圖片

第二種方案,使用映射工具庫,如MapStruct、ModelMapper等,它們可以自動生成屬性映射的代碼。這些工具庫可以減少手動編寫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);

責(zé)任編輯:武曉燕 來源: 撿田螺的小男孩
相關(guān)推薦

2024-06-04 00:10:00

開發(fā)拷貝

2024-02-27 10:42:04

開發(fā)代碼測試

2022-12-07 08:45:45

工具Springweb

2022-12-09 07:53:20

vo2dto方法AOP

2019-09-25 10:37:16

SpringBeanUtils接口

2024-04-10 08:39:56

BigDecimal浮點(diǎn)數(shù)二進(jìn)制

2020-06-12 11:03:22

Python開發(fā)工具

2018-01-20 20:46:33

2024-02-04 08:26:38

線程池參數(shù)內(nèi)存

2021-10-19 08:00:00

Windows 11Windows微軟

2021-08-14 09:48:02

ReentrantLock多線編程

2021-11-05 07:59:25

HashMapJava知識總結(jié)

2024-11-26 08:20:53

程序數(shù)據(jù)歸檔庫

2022-08-16 08:27:20

線程毀線程異步

2022-03-09 09:43:20

并發(fā)編程Java

2023-05-18 15:32:02

HTML開發(fā)技巧

2024-05-06 00:00:00

緩存高并發(fā)數(shù)據(jù)

2022-09-19 16:02:12

List代碼

2022-04-08 08:48:16

線上事故日志訂閱者

2021-10-12 07:57:26

Windows 11操作系統(tǒng)微軟
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號