日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot 如何进行对象复制,老鸟们都这么玩的!

發布時間:2025/3/15 javascript 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot 如何进行对象复制,老鸟们都这么玩的! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

??

??今天來聊聊在日常開發中如何優雅的實現對象復制。

首先我們看看為什么需要對象復制?

為什么需要對象復制

如上,是我們平時開發中最常見的三層MVC架構模型,編輯操作時Controller層接收到前端傳來的DTO對象,在Service層需要將DTO轉換成DO,然后在數據庫中保存。查詢操作時Service層查詢到DO對象后需要將DO對象轉換成VO對象,然后通過Controller層返回給前端進行渲染。

這中間會涉及到大量的對象轉換,很明顯我們不能直接使用getter/setter復制對象屬性,這看上去太low了。想象一下你業務邏輯中充斥著大量的getter&setter,代碼評審時老鳥們會如何笑話你?

所以我們必須要找一個第三方工具來幫我們實現對象轉換。

看到這里有同學可能會問,為什么不能前后端都統一使用DO對象呢?這樣就不存在對象轉換呀?

設想一下如果我們不想定義 DTO 和 VO,直接將 DO 用到數據訪問層、服務層、控制層和外部訪問接口上。此時該表刪除或則修改一個字段,DO 必須同步修改,這種修改將會影響到各層,這并不符合高內聚低耦合的原則。通過定義不同的 DTO 可以控制對不同系統暴露不同的屬性,通過屬性映射還可以實現具體的字段名稱的隱藏。不同業務使用不同的模型,當一個業務發生變更需要修改字段時,不需要考慮對其它業務的影響,如果使用同一個對象則可能因為 “不敢亂改” 而產生很多不優雅的兼容性行為。

?

對象復制工具類推薦

對象復制的類庫工具有很多,除了常見的Apache的BeanUtils,Spring的BeanUtils,Cglib BeanCopier,還有重量級組件MapStruct,Orika,Dozer,ModelMapper等。

如果沒有特殊要求,這些工具類都可以直接使用,除了Apache的BeanUtils。原因在于Apache BeanUtils底層源碼為了追求完美,加了過多的包裝,使用了很多反射,做了很多校驗,所以導致性能較差,并在阿里巴巴開發手冊上強制規定避免使用 Apache BeanUtils。

強制規定避免使用 Apache BeanUtils

至于剩下的重量級組件,綜合考慮其性能還有使用的易用性,我這里更推薦使用Orika。Orika底層采用了javassist類庫生成Bean映射的字節碼,之后直接加載執行生成的字節碼文件,在速度上比使用反射進行賦值會快很多。

國外大神 baeldung 已經對常見的組件性能進行過詳細測試,大家可以通過 https://www.baeldung.com/java-performance-mapping-frameworks 查看。

?

Orika基本使用

要使用Orika很簡單,只需要簡單四步:

  • 引入依賴

  • <dependency><groupId>ma.glasnost.orika</groupId><artifactId>orika-core</artifactId><version>1.5.4</version> </dependency>
  • 構造一個MapperFactory

  • MapperFactory?mapperFactory?=?new?DefaultMapperFactory.Builder().build();
  • 注冊字段映射

  • mapperFactory.classMap(SourceClass.class,?TargetClass.class)??.field("firstName",?"givenName").field("lastName",?"sirName").byDefault().register();

    當字段名在兩個實體不一致時可以通過.field()方法進行映射,如果字段名都一樣則可省略,byDefault()方法用于注冊名稱相同的屬性,如果不希望某個字段參與映射,可以使用exclude方法。

  • 進行映射

  • MapperFacade?mapper?=?mapperFactory.getMapperFacade();SourceClass?source?=?new?SourceClass();?? //?set?some?field?values ... //?map?the?fields?of?'source'?onto?a?new?instance?of?PersonDest TargetClass?target?=?mapper.map(source,?TargetClass.class);

    經過上面四步我們就完成了SourceClass到TargetClass的轉換。至于Orika的其他使用方法大家可以參考 http://orika-mapper.github.io/orika-docs/index.html

    看到這里,肯定有粉絲會說:你這推薦的啥玩意呀,這個Orika使用也不簡單呀,每次都要這先創建MapperFactory,建立字段映射關系,才能進行映射轉換。

    別急,我這里給你準備了一個工具類OrikaUtils,你可以通過文末github倉庫獲取。

    它提供了五個公共方法:

    分別對應:

  • 字段一致實體轉換

  • 字段不一致實體轉換(需要字段映射)

  • 字段一致集合轉換

  • 字段不一致集合轉換(需要字段映射)

  • 字段屬性轉換注冊

  • 接下來我們通過單元測試案例重點介紹此工具類的使用。

    ?

    Orika工具類使用文檔

    先準備兩個基礎實體類,Student,Teacher。

    @Data @AllArgsConstructor @NoArgsConstructor public?class?Student?{private?String?id;private?String?name;private?String?email; }@Data @AllArgsConstructor @NoArgsConstructor public?class?Teacher?{private?String?id;private?String?name;private?String?emailAddress; }

    TC1,基礎實體映射

    /***?只拷貝相同的屬性*/ @Test public?void?convertObject(){Student?student?=?new?Student("1","javadaily","jianzh5@163.com");Teacher?teacher?=?OrikaUtils.convert(student,?Teacher.class);System.out.println(teacher); }

    輸出結果:

    Teacher(id=1,?name=javadaily,?emailAddress=null)

    此時由于屬性名不一致,無法映射字段email。

    TC2,實體映射 - 字段轉換

    /***?拷貝不同屬性*/ @Test public?void?convertRefObject(){Student?student?=?new?Student("1","javadaily","jianzh5@163.com");Map<String,String>?refMap?=?new?HashMap<>(1);//map?key?放置?源屬性,value?放置?目標屬性refMap.put("email","emailAddress");Teacher?teacher?=?OrikaUtils.convert(student,?Teacher.class,?refMap);System.out.println(teacher); }

    輸出結果:

    Teacher(id=1,?name=javadaily,?emailAddress=jianzh5@163.com)

    此時由于對字段做了映射,可以將email映射到emailAddress。注意這里的refMap中key放置的是源實體的屬性,而value放置的是目標實體的屬性,不要弄反了。

    TC3,基礎集合映射

    /***?只拷貝相同的屬性集合*/ @Test public?void?convertList(){Student?student1?=?new?Student("1","javadaily","jianzh5@163.com");Student?student2?=?new?Student("2","JAVA日知錄","jianzh5@xxx.com");List<Student>?studentList?=?Lists.newArrayList(student1,student2);List<Teacher>?teacherList?=?OrikaUtils.convertList(studentList,?Teacher.class);System.out.println(teacherList); }

    輸出結果:

    [Teacher(id=1,?name=javadaily,?emailAddress=null),?Teacher(id=2,?name=JAVA日知錄,?emailAddress=null)]

    此時由于屬性名不一致,集合中無法映射字段email。

    TC4,集合映射 - 字段映射

    /***?映射不同屬性的集合*/ @Test public?void?convertRefList(){Student?student1?=?new?Student("1","javadaily","jianzh5@163.com");Student?student2?=?new?Student("2","JAVA日知錄","jianzh5@xxx.com");List<Student>?studentList?=?Lists.newArrayList(student1,student2);Map<String,String>?refMap?=?new?HashMap<>(2);//map?key?放置?源屬性,value?放置?目標屬性refMap.put("email","emailAddress");List<Teacher>?teacherList?=?OrikaUtils.convertList(studentList,?Teacher.class,refMap);System.out.println(teacherList); }

    輸出結果:

    [Teacher(id=1,?name=javadaily,?emailAddress=jianzh5@163.com),?Teacher(id=2,?name=JAVA日知錄,?emailAddress=jianzh5@xxx.com)]

    也可以通過這樣映射:

    Map<String,String>?refMap?=?new?HashMap<>(2); refMap.put("email","emailAddress"); List<Teacher>?teacherList?=?OrikaUtils.classMap(Student.class,Teacher.class,refMap).mapAsList(studentList,Teacher.class);

    TC5,集合與實體映射

    有時候我們需要將集合數據映射到實體中,如Person類

    @Data public?class?Person?{private?List<String>?nameParts; }

    現在需要將Person類nameParts的值映射到Student中,可以這樣做

    /***?數組和List的映射*/ @Test public?void?convertListObject(){Person?person?=?new?Person();person.setNameParts(Lists.newArrayList("1","javadaily","jianzh5@163.com"));Map<String,String>?refMap?=?new?HashMap<>(2);//map?key?放置?源屬性,value?放置?目標屬性refMap.put("nameParts[0]","id");refMap.put("nameParts[1]","name");refMap.put("nameParts[2]","email");Student?student?=?OrikaUtils.convert(person,?Student.class,refMap);System.out.println(student); }

    輸出結果:

    Student(id=1,?name=javadaily,?email=jianzh5@163.com)

    TC6,類類型映射

    有時候我們需要類類型對象映射,如BasicPerson類

    @Data public?class?BasicPerson?{private?Student?student; }

    現在需要將BasicPerson映射到Teacher

    /***?類類型映射*/ @Test public?void?convertClassObject(){BasicPerson?basicPerson?=?new?BasicPerson();Student?student?=?new?Student("1","javadaily","jianzh5@163.com");basicPerson.setStudent(student);Map<String,String>?refMap?=?new?HashMap<>(2);//map?key?放置?源屬性,value?放置?目標屬性refMap.put("student.id","id");refMap.put("student.name","name");refMap.put("student.email","emailAddress");Teacher?teacher?=?OrikaUtils.convert(basicPerson,?Teacher.class,refMap);System.out.println(teacher); }

    輸出結果:

    Teacher(id=1,?name=javadaily,?emailAddress=jianzh5@163.com)

    TC7,多重映射

    有時候我們會遇到多重映射,如將StudentGrade映射到TeacherGrade

    @Data public?class?StudentGrade?{private?String?studentGradeName;private?List<Student>?studentList; }@Data public?class?TeacherGrade?{private?String?teacherGradeName;private?List<Teacher>?teacherList; }

    這種場景稍微復雜,Student與Teacher的屬性有email字段不相同,需要做轉換映射;StudentGrade與TeacherGrade中的屬性也需要映射。

    /***?一對多映射*/ @Test public?void?convertComplexObject(){Student?student1?=?new?Student("1","javadaily","jianzh5@163.com");Student?student2?=?new?Student("2","JAVA日知錄","jianzh5@xxx.com");List<Student>?studentList?=?Lists.newArrayList(student1,student2);StudentGrade?studentGrade?=?new?StudentGrade();studentGrade.setStudentGradeName("碩士");studentGrade.setStudentList(studentList);Map<String,String>?refMap1?=?new?HashMap<>(1);//map?key?放置?源屬性,value?放置?目標屬性refMap1.put("email","emailAddress");OrikaUtils.register(Student.class,Teacher.class,refMap1);Map<String,String>?refMap2?=?new?HashMap<>(2);//map?key?放置?源屬性,value?放置?目標屬性refMap2.put("studentGradeName",?"teacherGradeName");refMap2.put("studentList",?"teacherList");TeacherGrade?teacherGrade?=?OrikaUtils.convert(studentGrade,TeacherGrade.class,refMap2);System.out.println(teacherGrade); }

    多重映射的場景需要根據情況調用OrikaUtils.register()注冊字段映射。

    輸出結果:

    TeacherGrade(teacherGradeName=碩士,?teacherList=[Teacher(id=1,?name=javadaily,?emailAddress=jianzh5@163.com),?Teacher(id=2,?name=JAVA日知錄,?emailAddress=jianzh5@xxx.com)])

    TC8,MyBaits plus分頁映射

    如果你使用的是mybatis的分頁組件,可以這樣轉換

    public?IPage<UserDTO>?selectPage(UserDTO?userDTO,?Integer?pageNo,?Integer?pageSize)?{Page?page?=?new?Page<>(pageNo,?pageSize);LambdaQueryWrapper<User>?query?=?new?LambdaQueryWrapper();if?(StringUtils.isNotBlank(userDTO.getName()))?{query.like(User::getKindName,userDTO.getName());}IPage<User>?pageList?=?page(page,query);//?實體轉換?SysKind轉化為SysKindDtoMap<String,String>?refMap?=?new?HashMap<>(3);refMap.put("kindName","name");refMap.put("createBy","createUserName");refMap.put("createTime","createDate");return?pageList.convert(item?->?OrikaUtils.convert(item,?UserDTO.class,?refMap)); }

    ?

    小結

    在MVC架構中肯定少不了需要用到對象復制,屬性轉換的功能,借用Orika組件,可以很簡單實現這些功能。本文在Orika的基礎上封裝了工具類,進一步簡化了Orika的操作,希望對各位有所幫助。

    有道無術,術可成;有術無道,止于術

    歡迎大家關注Java之道公眾號

    好文章,我在看??

    總結

    以上是生活随笔為你收集整理的SpringBoot 如何进行对象复制,老鸟们都这么玩的!的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 美女的奶胸大爽爽大片 | 久草资源网 | 激情综合五月天 | 亚洲色图另类图片 | 国产资源精品 | 国产精品一区二区免费看 | 色狠狠一区二区 | 成人午夜免费毛片 | 日韩激情一区 | 亚洲无人区码一码二码三码 | www.国产在线观看 | 天天看毛片 | 中文字幕av专区 | 插综合| 日本一区二区在线不卡 | 久久久久久亚洲av无码专区 | 久久亚洲精品石原莉奈 | 久久露脸国语精品国产91 | 日本黄色免费 | 精品一区二区三区四区五区六区 | 少妇综合 | 伊人看片 | 在线伊人网 | 久操免费在线视频 | av片免费观看 | 青青色在线视频 | 日韩综合第一页 | 国产操视频 | 日韩精品一区二区三区免费视频 | 一区二区三区波多野结衣 | 日本黄色美女网站 | 97色吧| 欧美一级免费看 | 成人av免费网站 | 国产精品无码一区二区无人区多人 | 成年人视频在线看 | 欧美三级午夜理伦三级小说 | 国产69精品一区二区 | 久久久久久黄色片 | 精品国产一区二区三区无码 | 国产美女久久久 | 国产日韩精品在线 | 欧美三级韩国三级日本三斤在线观看 | 五月婷婷免费视频 | 亚欧美精品 | 亚洲影院一区二区三区 | 久久精品视频在线观看 | 国产综合精品一区二区三区 | 懂色av成人一区二区三区 | 波多野结衣在线观看一区 | 日日摸夜夜爽 | 男女猛烈无遮挡免费视频 | 91蝌蚪网 | 高h校园不许穿内裤h调教 | 熟女毛毛多熟妇人妻aⅴ在线毛片 | 免费人成在线观看网站 | 蜜桃99视频一区二区三区 | 九九热久久免费视频 | 99久精品视频 | 欧美熟妇乱码在线一区 | 亚洲一区二区在线 | 欧美成人做爰大片免费看黄石 | 日韩一二三四 | 岛国精品在线播放 | 手机福利在线 | 欧美激情一区二区三级高清视频 | 亚洲一级av毛片 | 人妻丰满熟妇av无码区hd | 午夜激情福利电影 | 3d成人动漫在线观看 | 91丨porny在线| 欧美三级理论片 | 粉嫩小泬无遮挡久久久久久 | 国产人妖在线播放 | 美日韩免费视频 | 日本在线网站 | 在线天堂www在线国语对白 | 中文字幕人妻丝袜二区 | 99精品在线看 | 丰满人妻熟妇乱偷人无码 | www.-级毛片线天内射视视 | 久草视频免费在线播放 | 日韩有码一区 | 疯狂做受xxxx国产 | 精品综合久久久久 | 五月天av网站| 自慰无码一区二区三区 | 伊人久艹 | 久久久精品免费 | 成人国产综合 | 狠狠操五月天 | 麻豆美女视频 | 精品无码黑人又粗又大又长 | 爱爱视频网址 | www欧美| 欧美黄色录像片 | 韩日精品视频 | 尤物在线观看 | 日本少妇一区二区三区 |