日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

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

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

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

d9228資訊網——每日最新資訊28at.com

一、BeanUtils.copyProperties是什么?

BeanUtils.copyProperties是一個對象拷貝的常用工具,Spring和Apache都提供了對應的靜態方法,兩者源碼如下:d9228資訊網——每日最新資訊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);}

通過上述兩個源碼方法可以發現:兩個方法中的入參源對象和目標對象 順序是反的,所以在使用時,一定要注意具體導入的是哪一個BeanUtils,切勿把入參順序搞反。d9228資訊網——每日最新資訊28at.com

接著,分別解析兩種方式的源碼實現原理:d9228資訊網——每日最新資訊28at.com

1.Spring實現

org.springframework.beans.BeanUtils的源碼實現如下圖:d9228資訊網——每日最新資訊28at.com

d9228資訊網——每日最新資訊28at.com

整個源碼的實現邏輯總結成下面 7個步驟:d9228資訊網——每日最新資訊28at.com

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

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

2.Apache實現

org.apache.commons.beanutils.BeanUtils的源碼實現如下圖:d9228資訊網——每日最新資訊28at.com

d9228資訊網——每日最新資訊28at.com

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

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

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

如下示例,當source內部Inner對象的 address字段更改了,target的也跟著變更了:d9228資訊網——每日最新資訊28at.com

d9228資訊網——每日最新資訊28at.com

二、為什么不推薦BeanUtils.copyProperties

在上面源碼分析的過程中可以發現:只有同時滿足下面 3個條件才能拷貝成功:d9228資訊網——每日最新資訊28at.com

  • source和target的字段需要有getter和setter方法
  • source和target的字段名稱需要相同
  • source和target的字段類型需要相同

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

假如,在開發中忘記寫getter和setter,使用BeanUtils.copyProperties拷貝不會有異常,但是業務邏輯上沒有達到預期,所以這種異常要么在測試中發現,要么需要跑真實的業務邏輯才能發現。d9228資訊網——每日最新資訊28at.com

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

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

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

三、替代方案

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

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

1.原始的setter和getter

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

  • 控制的粒度更細,更靈活
  • 性能比BeanUtils.copyProperties的反射更高效
  • 如果拷貝字段有名稱和類型更改或者setter和getter方法丟失,編譯期立馬能發現
  • 如下示例,可以將多個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) 使用示例d9228資訊網——每日最新資訊28at.com

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

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

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

首先,我們需要增加mapstruct的依賴:d9228資訊網——每日最新資訊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接口:d9228資訊網——每日最新資訊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);}

接著,定義兩個實體類:d9228資訊網——每日最新資訊28at.com

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

最后,寫一個測試類:d9228資訊網——每日最新資訊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    }}

上述代碼,在編譯器會自動創建一個TestMapperImpl實現類,如下圖:d9228資訊網——每日最新資訊28at.com

d9228資訊網——每日最新資訊28at.com

(2) 實現原理d9228資訊網——每日最新資訊28at.com

最后,總結下MapStruct實現原理:d9228資訊網——每日最新資訊28at.com

① 注解處理器機制d9228資訊網——每日最新資訊28at.com

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

② 注解掃描與處理d9228資訊網——每日最新資訊28at.com

MapStruct定義了@Mapper、@Mapping 等注解,編譯器會調用注解處理器來處理這些注解。d9228資訊網——每日最新資訊28at.com

③ 代碼生成d9228資訊網——每日最新資訊28at.com

MapStruct會根據注解信息,解析源類和目標類的結構,并生成相應的映射,大致有以下幾個步驟:d9228資訊網——每日最新資訊28at.com

  • 解析注解和類結構:MapStruct 解析@Mapper接口、方法簽名以及@Mapping注解,獲取源類和目標類的字段信息。
  • 生成映射方法:根據解析結果,生成具體的映射方法,并調用源類的getter方法獲取值并賦值給目標類的對應字段。
  • 處理復雜映射:對于嵌套對象、集合等復雜結構,MapStruct會遞歸生成相應的映射代碼

④ 類型安全與錯誤檢查d9228資訊網——每日最新資訊28at.com

在代碼生成過程中,MapStruct會進行類型檢查,確保源字段和目標字段的類型匹配,如果發現類型不匹配會報編譯時錯誤。d9228資訊網——每日最新資訊28at.com

⑤ 支持自定義d9228資訊網——每日最新資訊28at.com

MapStruct允許用戶自定義映射邏輯,比如下面的示例,通過qualifiedByName和 @Named注解實現了一個自定義的方法:d9228資訊網——每日最新資訊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門檻會高一點。d9228資訊網——每日最新資訊28at.com

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

具體如何選擇,可以根據團隊約定而定,如果是個人學習,優先推薦mapstruct,可以作為一個學習和實踐點。d9228資訊網——每日最新資訊28at.com

五、總結

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

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

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

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

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

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

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

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

標簽:
  • 熱門焦點
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
欧美日本免费| 午夜在线电影亚洲一区| 国产日韩欧美在线一区| 国产伦精品一区| 尤物yw午夜国产精品视频明星| 亚洲高清在线播放| 亚洲精品一区在线观看香蕉| 亚洲视频第一页| 久久国产主播精品| 欧美激情视频在线播放| 欧美亚洲成人网| 黄色亚洲精品| 日韩视频在线播放| 欧美一级欧美一级在线播放| 免费久久精品视频| 国产精品久久久久久久久动漫| 激情一区二区| 在线午夜精品| 久久蜜桃资源一区二区老牛| 欧美日韩一区二区免费视频| 国产真实久久| 在线午夜精品| 久久夜色精品国产噜噜av| 欧美视频一区二区三区| 在线播放一区| 午夜精品在线观看| 欧美啪啪成人vr| 精品91在线| 亚洲无毛电影| 欧美h视频在线| 在线亚洲欧美| 国产精品高潮在线| 狠狠色噜噜狠狠狠狠色吗综合| 在线中文字幕日韩| 久久精品一区蜜桃臀影院| 久久综合给合久久狠狠狠97色69| 久久久精品国产免费观看同学| 国产精品家教| 99精品欧美一区| 香蕉免费一区二区三区在线观看| 欧美日韩不卡| 国产精品视频九色porn| 亚洲男女自偷自拍| 欧美精品一区二区三区蜜桃| 亚洲欧美日韩一区| 欧美视频日韩视频| 亚洲一区视频在线| 亚洲福利视频二区| 久久动漫亚洲| 亚洲精品久久久久久久久久久久久 | 伊人狠狠色丁香综合尤物| 午夜欧美精品| 一本色道久久加勒比精品| 精品成人在线视频| 国产精品久久九九| 欧美日本不卡视频| 欧美一级久久久| 激情小说亚洲一区| 欧美99在线视频观看| 亚洲三级电影全部在线观看高清 | 欧美呦呦网站| 99视频+国产日韩欧美| 欧美啪啪一区| 蜜臀va亚洲va欧美va天堂| 久久大综合网| 蜜臀久久99精品久久久画质超高清| 女人色偷偷aa久久天堂| 欧美精品在线视频观看| 欧美日韩亚洲一区二区三区在线观看| 久久久五月婷婷| 久久久精品欧美丰满| 香蕉成人伊视频在线观看| 欧美三区在线观看| 国产真实乱偷精品视频免| 亚洲图片欧洲图片日韩av| 欧美日韩国产色视频| 亚洲日本aⅴ片在线观看香蕉| 久久一本综合频道| 韩国v欧美v日本v亚洲v| 国产精品亚洲一区二区三区在线| 久久国产乱子精品免费女| 一区二区视频免费完整版观看| 亚洲精品美女在线观看| 亚洲欧美综合v| 国产精品美女久久久浪潮软件 | 亚洲美女av电影| 欧美伦理91i| 一本久久综合亚洲鲁鲁| 欧美天堂亚洲电影院在线观看 | 欧美激情亚洲激情| 亚洲精品乱码久久久久久日本蜜臀 | 日韩亚洲欧美综合| 欧美日韩免费在线观看| 一区二区三区欧美| 国产精品嫩草99av在线| 性久久久久久久久| 国产一区二区三区在线观看精品 | 久久天堂国产精品| 亚洲国产欧美一区二区三区同亚洲| 欧美成人免费小视频| 亚洲免费高清| 国产精品久久久久久久久婷婷| 午夜天堂精品久久久久| 激情久久久久久久| 欧美黄色网络| 亚洲午夜精品17c| 国产午夜精品久久| 毛片一区二区三区| 99视频+国产日韩欧美| 国产精品丝袜白浆摸在线| 久久精品五月婷婷| 亚洲精品国产系列| 国产精品美女在线观看| 久久精品中文字幕免费mv| 亚洲国产日日夜夜| 国产精品乱码一区二三区小蝌蚪 | 亚洲一区二区视频在线| 国产在线不卡| 欧美国产一区二区| 亚洲欧美制服中文字幕| 尹人成人综合网| 夜夜嗨av一区二区三区网站四季av| 欧美亚洲成人精品| 欧美在线免费观看视频| 亚洲黄色大片| 国产精品视频导航| 欧美高清在线观看| 亚洲欧美综合精品久久成人| 在线观看三级视频欧美| 欧美日韩一区二区在线播放| 欧美在线视频一区二区三区| 亚洲精品久久久一区二区三区| 国产精品视频一| 欧美成人免费全部| 午夜精品久久久久久久久久久久| **性色生活片久久毛片| 国产精品国产a级| 免费的成人av| 午夜欧美不卡精品aaaaa| 亚洲国产精品悠悠久久琪琪| 国产精品久久久99| 卡通动漫国产精品| 亚洲欧美999| 亚洲精品一区二区三区蜜桃久 | 激情文学一区| 国产精品视区| 欧美日本不卡| 久久在线免费| 香蕉乱码成人久久天堂爱免费| 亚洲精品在线免费| 红桃视频成人| 国产精品一二一区| 欧美日韩国产区一| 噜噜噜在线观看免费视频日韩| 午夜精品久久| 99精品久久久| 亚洲第一精品夜夜躁人人爽| 国产精品资源| 欧美天堂亚洲电影院在线播放| 另类春色校园亚洲| 欧美一区二区久久久| 一本色道久久综合狠狠躁篇的优点 | 亚洲欧美国产视频| 亚洲精品美女免费| 极品av少妇一区二区| 国产麻豆成人精品| 欧美日韩综合| 欧美激情综合五月色丁香小说| 久久久久国产精品www| 亚洲欧美电影院| 夜夜嗨av一区二区三区四季av| 亚洲人成高清| 亚洲第一黄色| 亚洲福利在线视频| 一区二区三区在线看| 欧美日韩不卡视频| 美女免费视频一区| 久久久久久穴| 久久精品国产69国产精品亚洲 | 一本色道久久综合狠狠躁篇怎么玩| 亚洲成人中文| 伊人天天综合| 久久久欧美一区二区| 欧美一区二区高清| 亚洲欧美视频一区| 亚洲一区二区成人| 中文精品一区二区三区 | 亚洲欧美bt| 亚洲在线成人精品| 亚洲专区一区| 99pao成人国产永久免费视频| 影音先锋日韩资源| 久久三级视频| 久久精品一区四区| 久久精品观看| 久久久999精品免费| 久久精品一二三区| 久久久蜜桃一区二区人| 久久久亚洲精品一区二区三区| 久久久久久久国产| 久久夜色撩人精品| 男女激情久久| 欧美精品在线免费观看| 欧美视频官网|