新聞中心
本文轉(zhuǎn)載自微信公眾號「小姐姐味道」,作者姐養(yǎng)狗2號 。轉(zhuǎn)載本文請聯(lián)系小姐姐味道公眾號。

成都創(chuàng)新互聯(lián)公司服務項目包括鯉城網(wǎng)站建設、鯉城網(wǎng)站制作、鯉城網(wǎng)頁制作以及鯉城網(wǎng)絡營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關系等,向廣大中小型企業(yè)、政府機構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,鯉城網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟效益。目前,我們服務的客戶以成都為中心已經(jīng)輻射到鯉城省份的部分城市,未來相信會繼續(xù)擴大服務區(qū)域并繼續(xù)獲得客戶的支持與信任!
這些年寫Java寫多了,感覺Java是越來越丑。尤其是在玩了TypeScript之后,看到Java代碼總有一股想吐的感覺。這種思想的轉(zhuǎn)變,從側(cè)面上證明了,我并不是一個專一的人。
因為我是一條狗。
喜新厭舊,是我的本性,即使我把自己表現(xiàn)的很純潔。
按理說,牛x的人物并不需要關注語言層面這種較低級的問題。但是,無論是什么語言,各種屬性拷貝,是在工程上繞不開的問題。比如折騰人的VO、BO、DTO、DO等。
項目中的代碼,有六成,是在做這些無用的轉(zhuǎn)換和各種數(shù)據(jù)驗證。這個比例是我瞎謅的,但也相差無幾。
在Java中,有三種方式來處理這些屬性拷貝:
- 直接硬編碼,把代碼硬懟上去
- 使用各種BeanUtils,通過反射完成賦值
- 使用類似MapStruct的工具,直接在編譯期完成
其實嘛,哪一種都有利弊,有些東西雖然香,但實際用起來,還是要思量一下。個個打扮的花枝招展的,都是外在的皮囊。
本文主要介紹Mapstruct的使用,并從這香噴噴的工具中,聞一下其中變餿的味道。
1. 如何使用?
照例,需要在pom中加入依賴包,我們這里用的是1.4.1.Final版本。
org.mapstruct mapstruct ${org.mapstruct.version}
這還沒完,還需要在pom中的build部分,增加一個插件。搞這么復雜,是因為它的原理和lombok是一樣的,同樣通過APT在編譯器實現(xiàn)的。
這意味著,它的代碼,在編譯期就完成了。不需要反射,所以效率就和直接寫get、set,是一樣的。
org.apache.maven.plugins maven-compiler-plugin 3.8.1 1.8 1.8 org.mapstruct mapstruct-processor ${org.mapstruct.version} org.projectlombok lombok 1.18.16 org.projectlombok lombok-mapstruct-binding 0.2.0
這時候,我們就可以使用它提供的注解,方便的進行屬性拷貝了。
- @Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
- public interface Transform {
- Transform T = Mappers.getMapper(Transform.class);
- Member fromMemberEntity(MemberEntity entity);
- MemberEntity fromMember(Member member);
- }
上面是一段示例代碼。Mapper注解,標志著這是一個類型轉(zhuǎn)換工具(對象映射器),它提供了很多策略供我們選擇。直接寫接口文件,并不需要做一些額外的動作,mapstruct就知道你要干什么!
在傳統(tǒng)的編程中,如果Member的屬性非常的多,我們需要手工完成這個過程,代碼會非常的多。
使用Mapperstruct之后,這部分重復的勞動,工具都替我們做了。
瞧瞧下面這張圖!
上面的圖表明了,代碼在target下的generated-source目錄下生成,這就是我們上面添加的插件的功勞;代碼的內(nèi)容,其實就是一些非空判斷和get、set等。相同字段名相同類型的屬性,將會無差別的拷貝過去。
如果你的bean屬性非常的多,這個工具會讓你的代碼由幾百行,變成幾行!
2. 與其他方式比較
那mapstruct有什么優(yōu)勢么?為什么不直接使用BeanUtils?它們的效果一樣的啊,而且后者各種類庫都有提供。
主要原因,就是效率問題。
BeanUtils是通過反射實現(xiàn)的,效率肯定很低;而mapstuct是基于APT實現(xiàn)的,沒有性能損耗。
BeanUtils的屬性拷貝,在判斷空值和不同類型的屬性時,有很多障礙,會歇菜;而mapstruct有非常靈活的策略和轉(zhuǎn)化方式,自定義性比較強(后面會談到)。
3. 復雜場景
那下面我們就來看一個復雜的場景。
如果你的bean中,只有一些普通的屬性,那么使用mapstruct,就是如絲般的順滑。但總有一些異常情況,需要使用更高級的處理方式。
假設我想要由Unit轉(zhuǎn)化為ProductUnitEntity,但其中有個字段measureType它們的類型不一樣,我們就可以使用Mappings注解完成這個轉(zhuǎn)化。
- @Mappings({
- @Mapping(source = "measureType.value", target = "measureType")
- })
- ProductUnitEntity fromUnit(Unit v);
編譯后的代碼如下所示。有了source和target,就可以實現(xiàn)比BeanUtils更加牛x的行為。你甚至可以通過dateFormat做一些日期轉(zhuǎn)化之類的。
其實,上面的measureType是一個枚舉類型。如何將普通的類型轉(zhuǎn)化為枚舉類型呢?我們只需要提供一個default方法就ok了。mapstruct會判斷參數(shù)類型和返回值,所以說方法的名稱可以是任何合法的值。
- default Unit.MeasureType measureTypeIntegerToDomain(Integer value) {
- for (Unit.MeasureType s : Unit.MeasureType.values()) {
- if (s.getValue() == value) {
- return s;
- }
- }
- return null;
- }
那mapstruct能實現(xiàn)List之間的轉(zhuǎn)化么?也是可以的。下面兩行代碼,就能夠自動的補充for循環(huán),讓你的代碼更加簡潔。
- List
fromSkuEntityList(List v); - List
fromSkuList(List v);
End那么問題來了。
既然這么好的東西,那為什么現(xiàn)在的很多項目,都不用mapstruct,甚至連BeanUtils都不用,直接手工在那里get、set呢?
一種原因是,這些工具會大幅減少代碼量。mapstruct+hibernate-validate,一個管轉(zhuǎn)化,另一個管驗證,簡直就是以代碼行數(shù)論天下的公司的噩夢??冃档偷?
另一種原因就是,使用這些工具,并 不利于項目的重構(gòu) 。假如你在DTO里把a字段改成了b字段,mapstruct都貼心的為你忽略了這些變化。你的項目代碼并不會提示錯誤,風險將直接帶到運行時。
而使用get、set的方式,除了代碼量變的非常多以外,唯一的風險就是開發(fā)人員忘記了為某個新增的字段賦值。
在這種情況下,機器干的活,并不一定比人類可靠。所以使用mapstruct有一個大的前提:你的團隊,能夠通過約定,不給變量亂起名字,不亂重構(gòu)。如此,才能發(fā)揮它的價值。
作者簡介:小姐姐味道 (xjjdog),一個不允許程序員走彎路的公眾號。聚焦基礎架構(gòu)和Linux。十年架構(gòu),日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。我的個人微信xjjdog0,歡迎添加好友,進一步交流。
文章標題:喜新厭舊,是我的本性。今天就寵Mapstruct怎么了!
文章來源:http://www.dlmjj.cn/article/dhpsiig.html


咨詢
建站咨詢
