AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當(dāng)前位置:首頁 > 科技  > 軟件

為什么不推薦使用 BeanUtils.copyProperties?

來源: 責(zé)編: 時間:2024-06-05 17:45:21 143觀看
導(dǎo)讀在日常開發(fā)中,經(jīng)常涉及到 VO、DTO、DO等對象之間的屬性拷貝,為了避免使用原始的setter和getter方法,我們通常過借助一些三方工具,本文我們將聊聊某程序員使用BeanUtils.copyProperties工具,導(dǎo)致差點被開除的血淚史。一、Be

在日常開發(fā)中,經(jīng)常涉及到 VO、DTO、DO等對象之間的屬性拷貝,為了避免使用原始的setter和getter方法,我們通常過借助一些三方工具,本文我們將聊聊某程序員使用BeanUtils.copyProperties工具,導(dǎo)致差點被開除的血淚史。e8428資訊網(wǎng)——每日最新資訊28at.com

e8428資訊網(wǎng)——每日最新資訊28at.com

一、BeanUtils.copyProperties是什么?

BeanUtils.copyProperties是一個對象拷貝的常用工具,Spring和Apache都提供了對應(yīng)的靜態(tài)方法,兩者源碼如下:e8428資訊網(wǎng)——每日最新資訊28at.com

// org.springframework.beans.BeanUtilspublic static void copyProperties(Object source, Object target) throws BeansException {    copyProperties(source, target, null, (String[]) null);}// org.apache.commons.beanutils.BeanUtilspublic static void copyProperties(final Object dest, final Object orig)        throws IllegalAccessException, InvocationTargetException {    BeanUtilsBean.getInstance().copyProperties(dest, orig);}

通過上述兩個源碼方法可以發(fā)現(xiàn):兩個方法中的入?yún)⒃磳ο蠛湍繕?biāo)對象 順序是反的,所以在使用時,一定要注意具體導(dǎo)入的是哪一個BeanUtils,切勿把入?yún)㈨樞蚋惴础?span style="display:none">e8428資訊網(wǎng)——每日最新資訊28at.com

接著,分別解析兩種方式的源碼實現(xiàn)原理:e8428資訊網(wǎng)——每日最新資訊28at.com

1.Spring實現(xiàn)

org.springframework.beans.BeanUtils的源碼實現(xiàn)如下圖:e8428資訊網(wǎng)——每日最新資訊28at.com

e8428資訊網(wǎng)——每日最新資訊28at.com

整個源碼的實現(xiàn)邏輯總結(jié)成下面 7個步驟:e8428資訊網(wǎng)——每日最新資訊28at.com

  1. Spring的 BeanUtils拷貝,使用的是反射機制
  2. 先獲取target中所有字段以及它們的getter和setter方法
  3. 遍歷target的字段,如果字段有setter方法或者不是忽略對象則進行下一步操作,否則忽略
  4. 用target的字段去source中獲取對應(yīng)的值(通過getter方法),有值則進行下一步,否則忽略
  5. 獲source和target中同一個字段的類型,并且判斷類型是否相同,相同則繼續(xù)下一步,否則忽略
  6. 如果source和target的字段是非public,則通過反射修改權(quán)限
  7. 最后,通過反射完成賦值
  8. 通過源碼分析,我們能夠看出org.springframework.beans.BeanUtils的拷貝屏蔽了很多的異常,總結(jié)如下:
  • source和target的字段缺少getter和setter方法,拷貝失敗
  • source和target的字段名稱不同,拷貝失敗,即字段名相同才可以拷貝
  • source和target的字段類型不同,拷貝失敗,即類型相同才可以拷貝
  • 對于Map類型,無法拷貝

對于上述前 3種拷貝失敗的場景,編譯期間無法感知,一旦代碼上線大概率會出 bug,另外,因為使用的反射機制,性能略有影響。e8428資訊網(wǎng)——每日最新資訊28at.com

2.Apache實現(xiàn)

org.apache.commons.beanutils.BeanUtils的源碼實現(xiàn)如下圖:e8428資訊網(wǎng)——每日最新資訊28at.com

e8428資訊網(wǎng)——每日最新資訊28at.com

通過源碼我們能夠看出:Apache的實現(xiàn)其實是對Spring的一種增強,增加了DynaBean和Map兩種類型的拷貝,它們的實現(xiàn)都是采用反射機制。e8428資訊網(wǎng)——每日最新資訊28at.com

另外,Spring和 Apache的兩種實現(xiàn)方案都是淺拷貝,也就是說,如果對象中還有內(nèi)嵌對象,如果不做額外處理,拷貝會失敗。e8428資訊網(wǎng)——每日最新資訊28at.com

所謂淺拷貝,淺拷貝是一種復(fù)制對象的方式,它創(chuàng)建一個新對象,這個新對象是原對象的副本,但對于對象中引用類型的字段,淺拷貝只復(fù)制它們的引用,而不復(fù)制它們所指向的實際對象。換句話說,淺拷貝只拷貝對象的第一層屬性,對于屬性中的引用類型,只拷貝引用地址。e8428資訊網(wǎng)——每日最新資訊28at.com

如下示例,當(dāng)source內(nèi)部Inner對象的 address字段更改了,target的也跟著變更了:e8428資訊網(wǎng)——每日最新資訊28at.com

e8428資訊網(wǎng)——每日最新資訊28at.com

二、為什么不推薦BeanUtils.copyProperties

在上面源碼分析的過程中可以發(fā)現(xiàn):只有同時滿足下面 3個條件才能拷貝成功:e8428資訊網(wǎng)——每日最新資訊28at.com

  • source和target的字段需要有g(shù)etter和setter方法
  • source和target的字段名稱需要相同
  • source和target的字段類型需要相同

以上 3個條件缺失任何一個拷貝都會失敗,但是編譯器無法感知,對程序員不友好。e8428資訊網(wǎng)——每日最新資訊28at.com

假如,在開發(fā)中忘記寫getter和setter,使用BeanUtils.copyProperties拷貝不會有異常,但是業(yè)務(wù)邏輯上沒有達(dá)到預(yù)期,所以這種異常要么在測試中發(fā)現(xiàn),要么需要跑真實的業(yè)務(wù)邏輯才能發(fā)現(xiàn)。e8428資訊網(wǎng)——每日最新資訊28at.com

還有一種場景,假如source中有個money字段一開始被程序員A定義成double類型,后面被程序員B 修改成了BigDecimal,程序員B發(fā)現(xiàn)代碼沒有報錯,而且是一個小修改就直接上線了。e8428資訊網(wǎng)——每日最新資訊28at.com

1天后,有人反饋線上出問題了,經(jīng)過好一番努力地排查發(fā)現(xiàn),使用BeanUtils.copyProperties拷貝,source中的money字段是BigDecimal類型,而target的money字段是double類型,最終導(dǎo)致拷貝失敗,而這位差點被開除的程序員恰好是這種場景。e8428資訊網(wǎng)——每日最新資訊28at.com

基于上述描述,BeanUtils.copyProperties無法在編譯期間對拷貝字段的修改及時感知錯誤,假如公司上線規(guī)范不嚴(yán),或者回歸測試不全面,一旦出現(xiàn)上述字段名稱或者類型被修改,很大可能造成線上問題,所以需要慎用BeanUtils.copyProperties。e8428資訊網(wǎng)——每日最新資訊28at.com

三、替代方案

既然BeanUtils.copyProperties拷貝存在上述問題,那么,有沒有什么好的替代方案呢?e8428資訊網(wǎng)——每日最新資訊28at.com

有,通常替代方案有 2種:使用原始的setter和getter方法 和 MapStruct。e8428資訊網(wǎng)——每日最新資訊28at.com

1.原始的setter和getter

使用原始的setter和getter方法進行拷貝,雖然會編寫一些看似啰嗦的代碼,但是它具備以下優(yōu)點:e8428資訊網(wǎng)——每日最新資訊28at.com

  • 控制的粒度更細(xì),更靈活
  • 性能比BeanUtils.copyProperties的反射更高效
  • 如果拷貝字段有名稱和類型更改或者setter和getter方法丟失,編譯期立馬能發(fā)現(xiàn)
  • 如下示例,可以將多個Source的字段按需拷貝到Target上:
import java.util.UUID;public Target convetSourceToTarget(Source1 source1, Source2 source2) {    Target target = new Target();    target.setId(UUID.randomUUID().toString());    target.setName(source1.getName());    target.setAge(source1.getAge());    target.setAddress(source2.getAddress());}

2.MapStruct

(1) 使用示例e8428資訊網(wǎng)——每日最新資訊28at.com

MapStruct是一個很優(yōu)秀的 Java庫,也是用于簡化對象之間的拷貝工作,其主要特點如下:e8428資訊網(wǎng)——每日最新資訊28at.com

  • 編譯時生成代碼:MapStruct在編譯時生成映射代碼,避免了運行時的性能開銷
  • 類型安全:生成的代碼是類型安全的,編譯時即可發(fā)現(xiàn)映射錯誤
  • 易于使用:通過注解配置,使用簡單直觀

為了更好地說明 MapStruct,我們以一個示例進行說明:e8428資訊網(wǎng)——每日最新資訊28at.com

首先,我們需要增加mapstruct的依賴:e8428資訊網(wǎng)——每日最新資訊28at.com

// maven 依賴<dependency>    <groupId>org.mapstruct</groupId>    <artifactId>mapstruct</artifactId>    <version>1.5.2.Final</version></dependency>// gradle依賴implementation 'org.mapstruct:mapstruct:1.5.2.Final'

然后,定義一個Mapper接口:e8428資訊網(wǎng)——每日最新資訊28at.com

import org.mapstruct.Mapper;import org.mapstruct.Mapping;import org.mapstruct.factory.Mappers;@Mapperpublic interface TestMapper {    TestMapper INSTANCE = Mappers.getMapper(TestMapper.class);    /**     * 在Mapping中定義對象的 source和 target字段,     * 如果source和 target的類型不一樣,編譯期會報錯    */    @Mapping(source = "name", target = "fullName")     UserDTO toDTO(UserEntity entity);}

接著,定義兩個實體類:e8428資訊網(wǎng)——每日最新資訊28at.com

public class UserDTO {    private String fullName;    private int age;}public class UserEntity {    private String name;    private int age;}

最后,寫一個測試類:e8428資訊網(wǎng)——每日最新資訊28at.com

public class MapStructTest {    public static void main(String[] args) {        UserEntity entity = new UserEntity();        entity.setName("John");        entity.setAge(30);        UserDTO dto = TestMapper.INSTANCE.toDTO(entity);        System.out.println(dto.getFullName()); // 輸出: John        System.out.println(dto.getAge());  // 輸出: 30    }}

上述代碼,在編譯器會自動創(chuàng)建一個TestMapperImpl實現(xiàn)類,如下圖:e8428資訊網(wǎng)——每日最新資訊28at.com

e8428資訊網(wǎng)——每日最新資訊28at.com

(2) 實現(xiàn)原理e8428資訊網(wǎng)——每日最新資訊28at.com

最后,總結(jié)下MapStruct實現(xiàn)原理:e8428資訊網(wǎng)——每日最新資訊28at.com

① 注解處理器機制e8428資訊網(wǎng)——每日最新資訊28at.com

MapStruct使用了 Java的注解處理器機制,通過實現(xiàn)javax.annotation.processing.Processor接口,在編譯時掃描和處理特定的注解。e8428資訊網(wǎng)——每日最新資訊28at.com

② 注解掃描與處理e8428資訊網(wǎng)——每日最新資訊28at.com

MapStruct定義了@Mapper、@Mapping 等注解,編譯器會調(diào)用注解處理器來處理這些注解。e8428資訊網(wǎng)——每日最新資訊28at.com

③ 代碼生成e8428資訊網(wǎng)——每日最新資訊28at.com

MapStruct會根據(jù)注解信息,解析源類和目標(biāo)類的結(jié)構(gòu),并生成相應(yīng)的映射,大致有以下幾個步驟:e8428資訊網(wǎng)——每日最新資訊28at.com

  • 解析注解和類結(jié)構(gòu):MapStruct 解析@Mapper接口、方法簽名以及@Mapping注解,獲取源類和目標(biāo)類的字段信息。
  • 生成映射方法:根據(jù)解析結(jié)果,生成具體的映射方法,并調(diào)用源類的getter方法獲取值并賦值給目標(biāo)類的對應(yīng)字段。
  • 處理復(fù)雜映射:對于嵌套對象、集合等復(fù)雜結(jié)構(gòu),MapStruct會遞歸生成相應(yīng)的映射代碼

④ 類型安全與錯誤檢查e8428資訊網(wǎng)——每日最新資訊28at.com

在代碼生成過程中,MapStruct會進行類型檢查,確保源字段和目標(biāo)字段的類型匹配,如果發(fā)現(xiàn)類型不匹配會報編譯時錯誤。e8428資訊網(wǎng)——每日最新資訊28at.com

⑤ 支持自定義e8428資訊網(wǎng)——每日最新資訊28at.com

MapStruct允許用戶自定義映射邏輯,比如下面的示例,通過qualifiedByName和 @Named注解實現(xiàn)了一個自定義的方法:e8428資訊網(wǎng)——每日最新資訊28at.com

@Mapping(target = "tags", source = "tagSet", qualifiedByName = "defaultToEmptySet")UserEntity fromDO(UserDTO dto);@Named("defaultToEmptySet")default Set<String> defaultToEmptySet(Set<String> items) {    return items == null ? new LinkedHashSet<>() : items;}

四、如何選擇?

原始的setter和getter方法簡單且靈活,mapstruct通過注解的方式,比起原始的setter和getter門檻會高一點。e8428資訊網(wǎng)——每日最新資訊28at.com

兩種方式都是編譯行為,因此,一旦拷貝的字段發(fā)生改變能及時感知,對程序員比較友好。e8428資訊網(wǎng)——每日最新資訊28at.com

具體如何選擇,可以根據(jù)團隊約定而定,如果是個人學(xué)習(xí),優(yōu)先推薦mapstruct,可以作為一個學(xué)習(xí)和實踐點。e8428資訊網(wǎng)——每日最新資訊28at.com

五、總結(jié)

本文通過分析BeanUtils.copyProperties的源碼,總結(jié)了它的幾個缺點,綜合評估,建議慎用!e8428資訊網(wǎng)——每日最新資訊28at.com

接著,通過分析mapstruct的原理以及使用案例,它完美解決了BeanUtils.copyProperties的缺點,是對象拷貝很不錯的選擇。e8428資訊網(wǎng)——每日最新資訊28at.com

對于原始的setter和getter也是對象拷貝很不錯的選擇。e8428資訊網(wǎng)——每日最新資訊28at.com

溫馨建議:如果使用三方的工具類,一定要事先了解其優(yōu)缺點和安全性問題,這樣才能在使用過程中能做到心中有譜,處事不亂,避免拆盲盒導(dǎo)致不必要的事故。如果有更多的精力,再去研究下其原理,吸收他人優(yōu)秀的思維。e8428資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-92151-0.html為什么不推薦使用 BeanUtils.copyProperties?

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Python 函數(shù)三劍客 reduce、filter &amp; map

下一篇: 萬字聊一聊RocketMQ一條消息短暫而又精彩的一生

標(biāo)簽:
  • 熱門焦點
  • Mate60手機殼曝光 致敬自己的經(jīng)典設(shè)計

    8月3日消息,今天下午博主數(shù)碼閑聊站帶來了華為Mate60的第三方手機殼圖,可以讓我們在真機發(fā)布之前看看這款華為全新旗艦的大致輪廓。從曝光的圖片看,Mate 60背后攝像頭面積依然
  • 對標(biāo)蘋果的靈動島 華為帶來實況窗功能

    繼蘋果的靈動島之后,華為也在今天正式推出了“實況窗”功能。據(jù)今天鴻蒙OS 4.0的現(xiàn)場演示顯示,華為的實況窗可以更高效的展現(xiàn)出實時通知,比如鎖屏上就能看到外賣、打車、銀行
  • 企業(yè)采用CRM系統(tǒng)的11個好處

    客戶關(guān)系管理(CRM)軟件可以為企業(yè)提供很多的好處,從客戶保留到提高生產(chǎn)力。  CRM軟件用于企業(yè)收集客戶互動,以改善客戶體驗和滿意度。  CRM軟件市場規(guī)模如今超過580
  • 在線圖片編輯器,支持PSD解析、AI摳圖等

    自從我上次分享一個人開發(fā)仿造稿定設(shè)計的圖片編輯器到現(xiàn)在,不知不覺已過去一年時間了,期間我經(jīng)歷了裁員失業(yè)、面試找工作碰壁,寒冬下一直沒有很好地履行計劃.....這些就放在日
  • 自動化在DevOps中的力量:簡化軟件開發(fā)和交付

    自動化在DevOps中扮演著重要角色,它提升了DevOps的效能。通過自動化工具和方法,DevOps團隊可以實現(xiàn)以下目標(biāo):消除手動和重復(fù)性任務(wù)。簡化流程。在整個軟件開發(fā)生命周期中實現(xiàn)更
  • 每天一道面試題-CPU偽共享

    前言:了不起:又到了每天一到面試題的時候了!學(xué)弟,最近學(xué)習(xí)的怎么樣啊 了不起學(xué)弟:最近學(xué)習(xí)的還不錯,每天都在學(xué)習(xí),每天都在進步! 了不起:那你最近學(xué)習(xí)的什么呢? 了不起學(xué)弟:最近在學(xué)習(xí)C
  • 華為Mate60標(biāo)準(zhǔn)版細(xì)節(jié)曝光:經(jīng)典星環(huán)相機模組回歸

    這段時間以來,關(guān)于華為新旗艦的爆料日漸密集。據(jù)此前多方爆料,今年華為將開始恢復(fù)一年雙旗艦戰(zhàn)略,除上半年推出的P60系列外,往年下半年的Mate系列也將
  • 蘋果公司要求三星和LG Display生產(chǎn)「無邊框」OLED iPhone顯示屏

    據(jù) The Elec 報道,蘋果已要求其供應(yīng)商為未來的 iPhone 型號開發(fā)「無邊框」OLED 顯示面板。蘋果顯然已要求三星和 LG Display 開發(fā)新的 OLED 顯示面
  • iQOO Neo8系列或定檔5月23日:首發(fā)天璣9200+ 安卓跑分王者

    去年10月,iQOO推出了iQOO Neo7系列機型,不僅搭載了天璣9000+,而且是同價位唯一一款天璣9000+直屏旗艦,一經(jīng)上市便受到了用戶的廣泛關(guān)注。在時隔半年后,
Top