在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

MapperStruct的使用教程

Android編程精選 ? 來源:稀土掘金技術(shù)社區(qū) ? 作者:DrLauPen ? 2022-10-11 11:15 ? 次閱讀

前言

相信絕大多數(shù)的業(yè)務開發(fā)同學,日常的工作都離不開寫 getter、setter 方法。要么是將下游的 RPC 結(jié)果通過getter、setter 方法進行獲取組裝。要么就是將自己系統(tǒng)內(nèi)部的處理結(jié)果通過 getter、setter 方法處理成前端所需要的 VO 對象。

publicUserInfoVOoriginalCopyItem(UserDTOuserDTO){
UserInfoVOuserInfoVO=newUserInfoVO();
userInfoVO.setUserName(userDTO.getName());
userInfoVO.setAge(userDTO.getAge());
userInfoVO.setBirthday(userDTO.getBirthday());
userInfoVO.setIdCard(userDTO.getIdCard());
userInfoVO.setGender(userDTO.getGender());
userInfoVO.setIsMarried(userDTO.getIsMarried());
userInfoVO.setPhoneNumber(userDTO.getPhoneNumber());
userInfoVO.setAddress(userDTO.getAddress());
returnuserInfoVO;
}

傳統(tǒng)的方法一般是采用硬編碼,將每個對象的值都逐一設值。當然為了偷懶也會有采用一些 BeanUtil 簡約代碼的方式:

publicUserInfoVOutilCopyItem(UserDTOuserDTO){
UserInfoVOuserInfoVO=newUserInfoVO();
//采用反射、內(nèi)省機制實現(xiàn)拷貝
BeanUtils.copyProperties(userDTO,userInfoVO);
returnuserInfoVO;
}

但是,像 BeanUtils 這類通過反射、內(nèi)省等實現(xiàn)的框架,在速度上會帶來比較嚴重的影響。尤其是對于一些大字段、大對象而言,這個速度的缺陷就會越明顯。針對速度這塊我還專門進行了測試,對普通的 setter 方法、BeanUtils 的拷貝以及本次需要介紹的 mapperStruct 進行了一次對比。得到的耗時結(jié)果如下所示:(具體的運行代碼請見附錄)

運行次數(shù) setter方法耗時 BeanUtils拷貝耗時 MapperStruct拷貝耗時
1 2921528(1) 3973292(1.36) 2989942(1.023)
10 2362724(1) 66402953(28.10) 3348099(1.417)
100 2500452(1) 71741323(28.69) 2120820(0.848)
1000 3187151(1) 157925125(49.55) 5456290(1.711)
10000 5722147(1) 300814054(52.57) 5229080(0.913)
100000 19324227(1) 244625923(12.65) 12932441(0.669)

以上單位均為毫微秒。括號內(nèi)的為當前組件同 Setter 比較的比值。可以看到 BeanUtils 的拷貝耗時基本為 setter 方法的十倍、二十倍以上。而 MapperStruct 方法拷貝的耗時,則與 setter 方法相近。由此可見,簡單的 BeanUtils 確實會給服務的性能帶來很大的壓力。而 MapperStruct 拷貝則可以很好的解決這個問題。

下面我們就來介紹一下 MapperStruct 這個能夠很好提升我們代碼效率的工具。

使用教程

maven依賴

首先要導入 mapStruct 的 maven 依賴,這里我們選擇最新的版本 1.5.0.RC1。

...

1.5.0.RC1

...

//mapStructmaven依賴


org.mapstruct
mapstruct
${org.mapstruct.version}


...

//編譯的組件需要配置



org.apache.maven.plugins
maven-compiler-plugin
3.8.1

1.8 
1.8 


org.mapstruct
mapstruct-processor
${org.mapstruct.version}

 





在引入 maven 依賴后,我們首先來定義需要轉(zhuǎn)換的 DTO 及 VO 信息,主要包含的信息是名字、年齡、生日、性別等信息。

@Data
publicclassUserDTO{
privateStringname;

privateintage;

privateDatebirthday;

//1-男0-女
privateintgender;

privateStringidCard;

privateStringphoneNumber;

privateStringaddress;

privateBooleanisMarried;
}
@Data
publicclassUserInfoVO{
privateStringuserName;

privateintage;

privateDatebirthday;

//1-男0-女
privateintgender;

privateStringidCard;

privateStringphoneNumber;

privateStringaddress;

privateBooleanisMarried;
}

緊接著需要編寫相應的mapper類,以便生成相應的編譯類。

@Mapper
publicinterfaceInfoConverter{

InfoConverterINSTANT=Mappers.getMapper(InfoConverter.class);

@Mappings({
@Mapping(source="name",target="userName")
})
UserInfoVOconvert(UserDTOuserDto);
}

需要注意的是,因為 DTO 中的 name 對應的其實是 VO 中的 userName。因此需要在 converter 中顯式聲明。在編寫完對應的文件之后,需要執(zhí)行 maven 的 complie 命令使得 IDE 編譯生成對應的 Impl 對象。(自動生成)

148c8974-3cc7-11ed-9e49-dac502259ad0.jpg

到此,mapperStruct 的接入就算是完成了~。我們就可以在我們的代碼中使用這個拷貝類了。

publicUserInfoVOnewCopyItem(UserDTOuserDTO,inttimes){
UserInfoVOuserInfoVO=newUserInfoVO();
userInfoVO=InfoConverter.INSTANT.convert(userDTO);
returnuserInfoVO;
}

怎么樣,接入是不是很簡單~

FAQ

1、接入項目時,發(fā)現(xiàn)并沒有生成對應的編譯對象class,這個是什么原因?

答:可能的原因有如下幾個:

忘記編寫對應的 @Mapper 注解,因而沒有生成

沒有配置上述提及的插件 maven-compiler-plugin

沒有執(zhí)行 maven 的 Compile,IDE 沒有進行相應編譯

2、接入項目后發(fā)現(xiàn),我項目內(nèi)的 Lombok、@Data 注解不好使了,這怎么辦呢?

由于 Lombok 本身是對 AST 進行修改實現(xiàn)的,但是 mapStruct 在執(zhí)行的時候并不能檢測到 Lombok 所做的修改,因此需要額外的引入 maven 依賴lombok-mapstruct-binding。

......
1.5.0.RC1
0.2.0
1.18.20
......

......

org.mapstruct
mapstruct
${org.mapstruct.version}


org.projectlombok
lombok-mapstruct-binding
${lombok-mapstruct-binding.version}


org.projectlombok
lombok
${lombok.version}

更詳細的,mapperStruct 在官網(wǎng)中還提供了一個實現(xiàn) Lombok 及 mapStruct 同時并存的案例

「3、更多問題:」

歡迎查看MapStruct官網(wǎng)文檔,里面對各種問題都有更詳細的解釋及解答。

實現(xiàn)原理

在聊到 mapstruct 的實現(xiàn)原理之前,我們就需要先回憶一下 JAVA 代碼運行的過程。大致的執(zhí)行生成的流程如下所示:

149b0828-3cc7-11ed-9e49-dac502259ad0.png

可以直觀的看到,如果我們想不通過編碼的方式對程序進行修改增強,可以考慮對抽象語法樹進行相應的修改。而mapstruct 也正是如此做的。具體的執(zhí)行邏輯如下所示:

14b7d1a6-3cc7-11ed-9e49-dac502259ad0.jpg

為了實現(xiàn)該方法,mapstruct 基于JSR 269 實現(xiàn)了代碼。JSR 269 是 JDK 引進的一種規(guī)范。有了它,能夠在編譯期處理注解,并且讀取、修改和添加抽象語法樹中的內(nèi)容。JSR 269 使用 Annotation Processor 在編譯期間處理注解,Annotation Processor 相當于編譯器的一種插件,因此又稱為插入式注解處理。想要實現(xiàn) JSR 269,主要有以下幾個步驟:

繼承 AbstractProcessor 類,并且重寫 process 方法,在 process 方法中實現(xiàn)自己的注解處理邏輯。

在 META-INF/services 目錄下創(chuàng)建 javax.annotation.processing.Processor 文件注冊自己實現(xiàn)的 Annotation Processor。

通過實現(xiàn)AbstractProcessor,在程序進行 compile 的時候,會對相應的 AST 進行修改。從而達到目的。

publicvoidcompile(ListsourceFileObjects,
Listclassnames,
Iterableprocessors)
{
if(processors!=null&&processors.iterator().hasNext())
explicitAnnotationProcessingRequested=true;
//asaJavaCompilercanonlybeusedonce,throwanexceptionif
//ithasbeenusedbefore.
if(hasBeenUsed)
thrownewAssertionError("attempttoreuseJavaCompiler");
hasBeenUsed=true;

//forciblysettheequivalentof-Xlint:-options,sothatnofurther
//warningsaboutcommandlineoptionsaregeneratedfromthispointon
options.put(XLINT_CUSTOM.text+"-"+LintCategory.OPTIONS.option,"true");
options.remove(XLINT_CUSTOM.text+LintCategory.OPTIONS.option);

start_msec=now();

try{
initProcessAnnotations(processors);

//此處會調(diào)用到mapStruct中的processor類的方法.
delegateCompiler=
processAnnotations(
enterTrees(stopIfError(CompileState.PARSE,parseFiles(sourceFileObjects))),
classnames);

delegateCompiler.compile2();
delegateCompiler.close();
elapsed_msec=delegateCompiler.elapsed_msec;
}catch(Abortex){
if(devVerbose)
ex.printStackTrace(System.err);
}finally{
if(procEnvImpl!=null)
procEnvImpl.close();
}
}

關鍵代碼,在mapstruct-processor包中,有個對應的類MappingProcessor繼承了 AbstractProcessor,并實現(xiàn)其 process 方法。通過對 AST 進行相應的代碼增強,從而實現(xiàn)對最終編譯的對象進行修改的方法。

@SupportedAnnotationTypes({"org.mapstruct.Mapper"})
@SupportedOptions({"mapstruct.suppressGeneratorTimestamp","mapstruct.suppressGeneratorVersionInfoComment","mapstruct.unmappedTargetPolicy","mapstruct.unmappedSourcePolicy","mapstruct.defaultComponentModel","mapstruct.defaultInjectionStrategy","mapstruct.disableBuilders","mapstruct.verbose"})
publicclassMappingProcessorextendsAbstractProcessor{
publicbooleanprocess(Setannotations,RoundEnvironmentroundEnvironment){
if(!roundEnvironment.processingOver()){
RoundContextroundContext=newRoundContext(this.annotationProcessorContext);
SetdeferredMappers=this.getAndResetDeferredMappers();
this.processMapperElements(deferredMappers,roundContext);
Setmappers=this.getMappers(annotations,roundEnvironment);
this.processMapperElements(mappers,roundContext);
}elseif(!this.deferredMappers.isEmpty()){
Iteratorvar8=this.deferredMappers.iterator();

while(var8.hasNext()){
MappingProcessor.DeferredMapperdeferredMapper=(MappingProcessor.DeferredMapper)var8.next();
TypeElementdeferredMapperElement=deferredMapper.deferredMapperElement;
ElementerroneousElement=deferredMapper.erroneousElement;
StringerroneousElementName;
if(erroneousElementinstanceofQualifiedNameable){
erroneousElementName=((QualifiedNameable)erroneousElement).getQualifiedName().toString();
}else{
erroneousElementName=erroneousElement!=null?erroneousElement.getSimpleName().toString():null;
}

deferredMapperElement=this.annotationProcessorContext.getElementUtils().getTypeElement(deferredMapperElement.getQualifiedName());
this.processingEnv.getMessager().printMessage(Kind.ERROR,"Noimplementationwascreatedfor"+deferredMapperElement.getSimpleName()+"duetohavingaproblemintheerroneouselement"+erroneousElementName+".Hint:thisoftenmeansthatsomeotherannotationprocessorwassupposedtoprocesstheerroneouselement.YoucanalsoenableMapStructverbosemodebysetting-Amapstruct.verbose=trueasacompilationargument.",deferredMapperElement);
}
}

returnfalse;
}
}

「如何斷點調(diào)試:」

因為這個注解處理器是在解析->編譯的過程完成,跟普通的 jar 包調(diào)試不太一樣,maven 框架為我們提供了調(diào)試入口,需要借助 maven 才能實現(xiàn) debug。所以需要在編譯過程打開 debug 才可調(diào)試。

在項目的 pom 文件所在目錄執(zhí)行 mvnDebug compile

接著用 idea 打開項目,添加一個 remote,端口為 8000

打上斷點,debug 運行 remote 即可調(diào)試。

14cd29f2-3cc7-11ed-9e49-dac502259ad0.jpg

附錄

測試代碼如下,采用Spock框架 + JAVA代碼 實現(xiàn)。Spock框架作為當前最火熱的測試框架,你值得學習一下。Spock框架初體驗:更優(yōu)雅地寫好你的單元測試

//@Resource
@Shared
MapperStructServicemapperStructService

defsetupSpec(){
mapperStructService=newMapperStructService()
}

@Unroll
def"testmapperStructTesttimes=#times"(){
given:"初始化數(shù)據(jù)"
UserDTOdto=newUserDTO(name:"笑傲菌",age:20,idCard:"1234",
phoneNumber:"18211932334",address:"北京天安門",gender:1,
birthday:newDate(),isMarried:false)

when:"調(diào)用方法"
//傳統(tǒng)的getter、setter拷貝
longstartTime=System.nanoTime();
UserInfoVOoldRes=mapperStructService.originalCopyItem(dto,times)
DurationoriginalWasteTime=Duration.ofNanos(System.nanoTime()-startTime);

//采用工具實現(xiàn)反射類的拷貝
longstartTime1=System.nanoTime();
UserInfoVOutilRes=mapperStructService.utilCopyItem(dto,times)
DurationutilWasteTime=Duration.ofNanos(System.nanoTime()-startTime1);

longstartTime2=System.nanoTime();
UserInfoVOmapStructRes=mapperStructService.newCopyItem(dto,times)
DurationmapStructWasteTime=Duration.ofNanos(System.nanoTime()-startTime2);

then:"校驗數(shù)據(jù)"
println("times="+times)
println("原始拷貝的消耗時間為:"+originalWasteTime.getNano())
println("BeanUtils拷貝的消耗時間為:"+utilWasteTime.getNano())
println("mapStruct拷貝的消耗時間為:"+mapStructWasteTime.getNano())
println()

where:"比較不同次數(shù)調(diào)用的耗時"
times||ignore
1||null
10||null
100||null
1000||null
}

測試的Service如下所示:

publicclassMapperStructService{

publicUserInfoVOnewCopyItem(UserDTOuserDTO,inttimes){
UserInfoVOuserInfoVO=newUserInfoVO();
for(inti=0;i

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • RPC
    RPC
    +關注

    關注

    0

    文章

    111

    瀏覽量

    11587
  • 代碼
    +關注

    關注

    30

    文章

    4841

    瀏覽量

    69176

原文標題:用了這個工具后,再也不寫 getter、setter 了!

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    主站蜘蛛池模板: 男男h啪肉np文总受 男男h全肉耽污 | 中文字幕欧美日韩 | 中国一级做a爰片久久毛片 中韩日欧美电影免费看 | 久久国产精品免费专区 | 日本午夜片成年www 日本午夜三级 | 欧美第四色 | 色婷婷综合久久久久中文一区二区 | h小视频在线观看 | 久久精品人人做人人看 | 黄色片 720p 黄色片链接 | 免费又爽又黄1000禁片 | 永久免费在线看 | 视频一区视频二区在线观看 | 欧美一级特黄视频 | 久久一级毛片 | jizz 大全欧美| 欧美在线小视频 | 国产亚洲精品免费 | 天堂资源在线www中文 | 日韩99| 日本一本在线视频 | 色偷偷免费 | 免费网站在线视频美女被 | 日本免费不卡一区 | 狠狠色噜噜狠狠狠狠米奇777 | 四虎在线观看免费永久 | 奇米影视亚洲狠狠色777不卡 | 久久美女精品国产精品亚洲 | 天天操天天操 | 亚洲精品黄色 | 亚洲高清视频一区 | 好爽好黄的视频 | 久久午夜宅男免费网站 | 特黄特黄aaaa级毛片免费看 | 爱婷婷网站在线观看 | 中文字幕第一区 | 亚洲综合日韩欧美一区二区三 | 成年网站在线在免费播放 | 日韩欧美一级 | 五月激情综合网 | 五月婷婷丁香在线观看 |