




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
第Java對象轉(zhuǎn)換的方案分享目錄前言為什么模型要分這么多層?模型之間的轉(zhuǎn)換建議不要用的方式常用的方式使用方式定義對象BeanCopier最簡單的使用方式創(chuàng)建可復(fù)用的BeanCopier工具類MapStruct引入mapstruct簡單Demo常見用法性能測試測試代碼測試結(jié)果最后
前言
系統(tǒng)變的復(fù)雜,系統(tǒng)的層次劃分越來越細(xì),邊界也越來越明確。然后每一層之間一般都有自己要處理的領(lǐng)域?qū)ο螅y(tǒng)稱為pojo一般在model或者domain包下(類的后綴不能為pojo)。
常見的一些模型類型:
PO、DO:持久層對象,一般和數(shù)據(jù)庫直接打交道。DTO:數(shù)據(jù)傳輸對象,系統(tǒng)之間的交互,再服務(wù)層提供服務(wù)的時候輸出到其它系統(tǒng)。VO:視圖對象,用于前端模型展示。當(dāng)然有時候前端也可以看做另外一個系統(tǒng),使用DTO模型;BO:業(yè)務(wù)邏輯對象,比較少用...
為什么模型要分這么多層?
在復(fù)雜一點的業(yè)務(wù)中,業(yè)務(wù)建模是非常有必要的,一定要抽象出業(yè)務(wù)上常用的領(lǐng)域模型,統(tǒng)一技術(shù)和非技術(shù)同學(xué)的語言。
建完模型之后,在技術(shù)的系統(tǒng)中,為了方便維護(hù)代碼,分離關(guān)注點,也會進(jìn)行再次分層,讓每一層解決特定的問題。模型的分層是隨著系統(tǒng)的分層而來的;試想所有的模型屬性在一個對象中,這個對象你看的懂嗎?
舉個實際的案例:
數(shù)據(jù)層一般用DO現(xiàn)在要透出數(shù)據(jù)給其他系統(tǒng),DO中一般都會有創(chuàng)建人,創(chuàng)建時間,修改人,修改時間,當(dāng)前對象所處的環(huán)境等信息;但是外部的系統(tǒng)需要環(huán)境、創(chuàng)建人信息嗎?很多時候不需要,站在數(shù)據(jù)安全的角度,一般只透出必要的字段就可以;這些要輸出要外部系統(tǒng)的必要字段,一般定義在DTO中。到前端系統(tǒng),前端系統(tǒng)展示上所需的邏輯和輸出到外部系統(tǒng)的又有點不太一樣,前端系統(tǒng)可能要創(chuàng)建人,創(chuàng)建時間,但是不要另外一些東西,或者一些敏感的配置不能透出給前端,這個時候一般也會再定義一個新的對象。
簡單說就是當(dāng)我們的系統(tǒng)要輸出能力到外部系統(tǒng)的時候,不同系統(tǒng)要的數(shù)據(jù)不一樣,數(shù)據(jù)安全要求我們不能透出這么多的數(shù)據(jù),一定要做一層處理。另外給另外一個系統(tǒng)關(guān)注的數(shù)據(jù),而不是一股腦的全部都給對方,對方處理起來也方便。
模型之間的轉(zhuǎn)換
建議不要用的方式
手寫get\set;雖然性能高,但是費勁并且眼花繚亂,一不小心就寫錯了,難以維護(hù),復(fù)用度不高BeanUtils,apacha和spring包下都有對應(yīng)的類,但是底層用到的都是反射,性能比較差,大流量的情況下一般不用直接fastjson,gc會很頻繁,而且性能比較差
常用的方式
cglib的beanCopier,開銷在創(chuàng)建BeanCopier,一般在創(chuàng)建類的時候提前創(chuàng)建好一個,在代碼運行的時候直接進(jìn)行copy,性能接近原生。mapstruct性能和原生代碼一樣,支持復(fù)雜的轉(zhuǎn)化場景,實現(xiàn)原理同lombok編譯的時候生成對應(yīng)的代碼。
以上從技術(shù)分類的角度來看:
反射:fastjson,beanutil都不建議用get\set:beancoper通過字節(jié)碼進(jìn)行g(shù)etset,mapstruct編譯的時候生成getset。性能相對較好。
使用方式
個人覺得,如果說對象比較簡單的時候,使用BeanCopier就可以了,因為spring的aop依賴cglib,默認(rèn)情況下就已經(jīng)引入了對應(yīng)的包了,不需要額外的依賴直接就可以用。
如果很復(fù)雜的模型之間的轉(zhuǎn)換,并且對性能有更極致的要求,考慮使用下MapStruct。
定義對象
UserDO
@Data
publicclassUserDO{
privateLongid;
privateStringname;
privateIntegergender;
privateStringpassword;
privateDategmtCreate;
privateDategmtModified;
}
UserDTO
@Data
publicclassUserDTO{
privateLongid;
privateStringname;
privateIntegergender;
}
BeanCopier
最簡單的使用方式
BeanCopierbeanCopier=BeanCopier.create(UserDO.class,UserDTO.class,false);bean.copy即可;
privatestaticvoidsimpleBeanCopy(){
BeanCopierbeanCopier=BeanCopier.create(UserDO.class,UserDTO.class,false);
UserDOuserDO=newUserDO();
userDO.setId(1L);
userDO.setName("aihe");
userDO.setGmtCreate(newDate());
userDO.setGender(0);
userDO.setPassword("xxxxxx");
UserDTOuserDTO=newUserDTO();
beanCopier.copy(userDO,userDTO,null);
Assert.assertEquals("名稱未成功拷貝",userDTO.getName(),"aihe");
Assert.assertEquals("Id未成功拷貝",1L,(long)userDTO.getId());
Assert.assertEquals("性別未成功拷貝",Integer.valueOf(0),userDTO.getGender());
}
創(chuàng)建可復(fù)用的BeanCopier工具類
packageme.aihe.daka;
importjava.util.Map;
importjava.util.concurrent.ConcurrentHashMap;
importnet.sf.cglib.beans.BeanCopier;
*@author:aihe
*@date:2025/9/129:21AM
*使用場景:
*功能描述:
publicclassBeanCopyUtils{
*beanCopier緩存
*由sourceClass和targetClass可以確定一個唯一的BeanCoper,因此使用二級Map;
privatestaticMapClass,MapClass,BeanCopierbeanCopierMap=newConcurrentHashMap();
*直接指定Bean對象進(jìn)行拷貝
*@paramsourceBean
*@paramtargetBean
*@paramS
*@paramT
publicstaticS,Tvoidcopy(SsourceBean,TtargetBean){
@SuppressWarnings("unchecked")
ClassSsourceClass=(ClassS)sourceBean.getClass();
@SuppressWarnings("unchecked")
ClassTtargetClass=(ClassT)targetBean.getClass();
BeanCopierbeanCopier=getBeanCopier(sourceClass,targetClass);
beanCopier.copy(sourceBean,targetBean,null);
*轉(zhuǎn)換方法
*@paramsourceBean原對象
*@paramtargetClass目標(biāo)類
*@paramS
*@paramT
*@return
publicstaticS,TTconvert(SsourceBean,ClassTtargetClass){
try{
assertsourceBean!=null;
TtargetBean=targetClass.newInstance();
copy(sourceBean,targetBean);
returntargetBean;
}catch(Exceptione){
thrownewRuntimeException(e);
privatestaticS,TBeanCopiergetBeanCopier(ClassSsourceClass,ClassTtargetClass){
MapClass,BeanCopiermap=beanCopierMap.get(sourceClass);
if(map==null||map.isEmpty()){
BeanCopiernewBeanCopier=BeanCopier.create(sourceClass,targetClass,false);
MapClass,BeanCopiernewMap=newConcurrentHashMap();
newMap.put(targetClass,newBeanCopier);
beanCopierMap.put(sourceClass,newMap);
returnnewBeanCopier;
BeanCopierbeanCopier=map.get(targetClass);
if(beanCopier==null){
BeanCopiernewBeanCopier=BeanCopier.create(sourceClass,targetClass,false);
map.put(targetClass,newBeanCopier);
returnnewBeanCopier;
returnbeanCopier;
}
同上:
UserDOuserDO=newUserDO();
userDO.setId(1L);
userDO.setName("aihe");
userDO.setGmtCreate(newDate());
userDO.setGender(0);
userDO.setPassword("xxxxxx");
UserDTOuserDTO=newUserDTO();
BeanCopyUtils.copy(userDO,userDTO);
Assert.assertEquals("名稱未成功拷貝",userDTO.getName(),"aihe");
Assert.assertEquals("Id未成功拷貝",1L,(long)userDTO.getId());
Assert.assertEquals("性別未成功拷貝",Integer.valueOf(0),userDTO.getGender());
MapStruct
案例集:/mapstruct/m
引入mapstruct
properties
project.build.sourceEncodingUTF-8/project.build.sourceEncoding
org.mapstruct.version1.5.2.Final/org.mapstruct.version
jectlombok.version1.18.20/jectlombok.version
/properties
dependencies
dependency
groupIdorg.mapstruct/groupId
artifactIdmapstruct/artifactId
version${org.mapstruct.version}/version
/dependency
dependency
groupIdorg.mapstruct/groupId
artifactIdmapstruct-processor/artifactId
version${org.mapstruct.version}/version
!--IntelliJdoesnotpickuptheprocessorifitisnotinthedependencies.
ThereisalreadyanopenissueforIntelliJsee/issue/IDEA-150621
scopeprovided/scope
/dependency
dependency
groupIdjectlombok/groupId
artifactIdlombok/artifactId
version${jectlombok.version}/version
scopeprovided/scope
/dependency
/dependencies
簡單Demo
定義Mapper
@Mapper
publicinterfaceUserDTOMapper{
UserDTOMapperMAPPER=Mappers.getMapper(UserDTOMapper.class);
//@Mapping(source="test",target="testing")
//@Mapping(source="test1",target="testing2")
UserDTOtoTarget(UserDOs);
}
使用:
publicstaticvoidmain(String[]args){
//simpleDemo();
UserDOuserDO=newUserDO();
userDO.setId(1L);
userDO.setName("aihe");
userDO.setGmtCreate(newDate());
userDO.setGender(0);
userDO.setPassword("xxxxxx");
UserDTOuserDTO=UserDTOMapper.MAPPER.toTarget(userDO);
Assert.assertEquals("名稱未成功拷貝",userDTO.getName(),"aihe");
Assert.assertEquals("Id未成功拷貝",1L,(long)userDTO.getId());
Assert.assertEquals("性別未成功拷貝",Integer.valueOf(0),userDTO.getGender());
}
常見用法
屬性類型相同,名稱不同的時候,使用@Mapping注解指定source和target字段名稱對應(yīng)關(guān)系,如果有多個這種屬性,那就指定多個@Mapping注解。忽略某個字段,在@Mapping的時候,加上ignore=true轉(zhuǎn)化日期格式,字符串到數(shù)字的格式,可以使用dateFormat,numberFormat如果有自定義轉(zhuǎn)換的需求,寫一個簡單的Java類即可,然后在方法上打上Mapstruct的注解@Named,在在@Mapper(uses=自定義的類),然后@Mapping中用上qualifiedByName。
@Mapping(target="userNick1",source="userNick")
@Mapping(target="createTime",source="createTime",dateFormat="yyyy-MM-dd")
@Mapping(target="age",source="age",numberFormat="#0.00")
@Mapping(target="id",ignore=true)
@Mapping(target="userVerified",defaultValue="defaultValue-2")
UserDTOtoTarget(UserDOs);
性能測試
測試代碼
importjava.util.Date;
importcom.alibaba.fastjson.JSON;
importorg.junit.Before;
importorg.junit.Test;
*@author:aiheaihe.ah@
*@date:2025/9/129:47AM
*使用場景:
*功能描述:
publicclassBenchDemoTest{
*轉(zhuǎn)化對象
privateUserDOuserDO;
*轉(zhuǎn)化次數(shù)
privatefinalstaticintcount=1000000;
@Before
publicvoidbefore(){
userDO=newUserDO();
userDO.setId(1L);
userDO.setName("aihe");
userDO.setGmtCreate(newDate());
userDO.setGender(0);
userDO.setPassword("xxxxxx");
@Test
publicvoidmapstruct(){
longstartTime=System.currentTimeMillis();
for(inti=1;i=count;i++){
UserDTOuserDTO=UserDTOMapper.MAPPER.toTarget(userDO);
System.out.println("mapstructtime"+(System.currentTimeMillis()-startTime));
@Test
publicvoidbeanCopier(){
longstartTime=System.currentTimeMillis();
for(inti=1;i=count;i++){
UserDTOtargetBean=newUserDTO();
BeanCopyUtils.copy(userDO,targetBean);
System.out.println("beanCopiertime"+(System.currentTimeMillis()-startTime));
@Test
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 制藥生產(chǎn)車間6S管理培訓(xùn)全攻略
- 寫字樓項目精準(zhǔn)招商與運營管理實戰(zhàn)培訓(xùn)
- 患者身份識別制度培訓(xùn)
- 城市建筑綠色建筑技術(shù)重點基礎(chǔ)知識點
- 建設(shè)工程安全培訓(xùn)課件
- 崗前培訓(xùn)教學(xué)方案設(shè)計與實施
- 《英國醫(yī)療保健》課件
- 特殊病種協(xié)議書
- 氟苯采購協(xié)議書
- 市場推廣戰(zhàn)略合作協(xié)議說明手冊
- 資質(zhì)掛靠設(shè)計院合同協(xié)議
- 2025年海淀高三二模語文試題及答案
- 新聞記者職業(yè)資格2024年筆試考試題庫含答案
- 2025-2030中國食品中的X射線檢查系統(tǒng)行業(yè)市場發(fā)展趨勢與前景展望戰(zhàn)略研究報告
- 陜西省多校聯(lián)考2024-2025學(xué)年高一下學(xué)期4月期中地理試題(原卷版+解析版)
- 人工智能應(yīng)用基礎(chǔ) 課件 第六章 大語言模型
- 施工流水作業(yè)段的劃分與組織畢業(yè)設(shè)計
- 跨文化團(tuán)隊協(xié)作研究-第1篇-全面剖析
- 鼻咽惡性腫瘤的護(hù)理查房
- 2024年輔導(dǎo)員招聘常識試題及答案
- ISO27001:2022信息安全管理體系全套文件+表單
評論
0/150
提交評論