<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          轉(zhuǎn)換Bean對象?BeanUtils?不是吧,阿 Sir

          共 5880字,需瀏覽 12分鐘

           ·

          2020-09-16 17:44

          本文公眾號來源:孤獨煙
          作者:孤獨煙
          本文已收錄至我的GitHub

          背景

          我們的故事要從一個風(fēng)和日麗的下午開始說起!

          這天,外包韓在位置上寫代碼~外包韓根據(jù)如下定義

          • PO(persistant object): 持久化對象,可以看成是與數(shù)據(jù)庫中的表相映射的 java 對象。最簡單的 PO 就是對應(yīng)數(shù)據(jù)庫中某個表中的一條記錄。
          • VO(view object): 視圖對象,用于展示層,它的作用是把某個指定頁面(或組件)的所有數(shù)據(jù)封裝起來。
          • BO(business object): 業(yè)務(wù)對象,主要作用是把業(yè)務(wù)邏輯封裝為一個對象。這個對象可以包括一個或多個其它的對象。
          • DTO、DO(省略......)

          將Bean進(jìn)行逐一分類!例如一個car_tb的表,于是他有了兩個類,一個叫CarPo,里頭屬性和表字段完全一致。另一個叫CarVo,用于頁面上的Car顯示!但是外包韓在做CarPo到CarVo轉(zhuǎn)換的時候,代碼是這么寫的,偽代碼如下:

          CarPo carPo = this.carDao.selectById(1L);CarVo carVo = new CarVo();carVo.setId(carPo.getId());carVo.setName(carPo.getName());//省略一堆return carVo;

          畫外音:看到這一串代碼是不是特別親切,我接手過一堆外包留下的代碼,就是這么寫的,一坨屎山!一類幾千行,一半都在set屬性。

          恰巧,阿雄打水路過!雞賊的阿雄瞄了一眼外包韓的屏幕,看到外包韓的這一系列代碼!上去進(jìn)行一頓教育,覺得不夠優(yōu)雅!阿雄覺得,應(yīng)該用BeanUtils.copyProperties來簡化書寫,像下面這樣!

          CarPo carPo = this.carDao.selectById(1L);CarVo carVo = new CarVo();BeanUtils.copyProperties(carPo, carVo);return carVo;

          可是,外包韓盯著這段代碼,說道:"網(wǎng)上不是說反射效率慢,你這么寫,沒有性能問題么?"?

          阿雄說道:" 如果是用Apache的BeanUtil類,確實有很大的性能問題,像阿里巴巴的代碼掃描插件,都禁止用該類,如下所示!"

          "但是,如果采用的是像Spring的BeanUtils類,要在調(diào)用次數(shù)足夠多的時候,你才能明顯的感受到卡頓。"阿雄補(bǔ)充道。

          "哇,阿雄真棒!"外包韓興奮不已!

          看著這辦公室基情滿滿的氛圍。一旁正在拖地的清潔工------掃地?zé)?/strong>,他決定不再沉默。

          只見掃地?zé)熑拥羰种械耐习眩蒙恼f道"我們不考慮性能。從拓展性角度看看!BeanUtils還是有很多問題的!"

          • 復(fù)制對象時字段類型不一致,導(dǎo)致賦值不上,你怎么解決?自己拓展?
          • 復(fù)制對象時字段名稱不一致,例如CarPo里叫carName,CarVo里叫name,導(dǎo)致賦值不上,你怎么解決?自己拓展?
          • 如果是集合類的復(fù)制,例如List轉(zhuǎn)換為List>,你怎么處理?
          • (省略一萬字....)

          "那應(yīng)該怎么辦呢?"聽了掃地?zé)煹拿枋觯獍n疑惑的問道!

          "很簡單,其實我們在轉(zhuǎn)換bean的過程中,set這些邏輯是固定的,唯一變化的就是轉(zhuǎn)換規(guī)則。因此,如果我們只需要書寫轉(zhuǎn)換規(guī)則,轉(zhuǎn)換代碼由系統(tǒng)根據(jù)規(guī)則自動生成,就方便很多了!還是用上面的例子,CarPo里叫carName,CarVo里叫name,屬性名稱不一致。我們就通過一個注解

          @Mapping(source?=?"carName",?target?=?"name")

          指定對應(yīng)轉(zhuǎn)換規(guī)則。系統(tǒng)識別到這個注解,就會生成代碼

          carVo.setName(carPo.getCarName())

          如果能以這樣的方式,set代碼由系統(tǒng)自動生成,那么靈活性將大大加強(qiáng),而且這種方式不存在性能問題!"掃地?zé)熝a(bǔ)充道!

          "那這些set邏輯,由什么工具來生成呢?"外包韓和阿雄一起問道!

          "嗯,這個工具的名字叫MapStruct!"

          ok,上面的故事到了這里,就結(jié)束了!不需要問結(jié)局,結(jié)局只有一個,外包韓和阿雄幸福美滿的...(省略10000字)...

          那么我們開始具體來說一說MapStruct

          MapStruct的教程

          這里從用法、原理、優(yōu)勢三個角度來介紹一下這個插件,至于詳細(xì)教程,還是看官方文檔吧。

          用法

          引入pom文件如下

          <dependency>    <groupId>org.mapstructgroupId>        <artifactId>mapstruct-jdk8artifactId>    <version>1.2.0.Finalversion>dependency><dependency>    <groupId>org.mapstructgroupId>    <artifactId>mapstruct-processorartifactId>    <version>1.2.0.Finalversion>dependency>

          在準(zhǔn)備兩個實體類,為了方便演示,用了lombok插件。準(zhǔn)備兩個實體類,一個是CarPo

          @Data@Builder@NoArgsConstructor@AllArgsConstructorpublic class CarPo {    private Integer id;    private String brand;}

          還有一個是CarVo

          @Data@Builder@NoArgsConstructor@AllArgsConstructorpublic class CarVo {    private Integer id;    private String brand;}

          再來一個轉(zhuǎn)換接口

          @Mapperpublic interface CarCovertBasic {    CarCovertBasic INSTANCE =     Mappers.getMapper(CarCovertBasic.class);        CarVo toConvertVo(CarPo source);}

          測試代碼如下:

          //實際中從數(shù)據(jù)庫取CarPo carPo = CarPo.builder().id(1)                           .brand("BMW")                           .build();CarVo carVo = CarCovertBasic.INSTANCE.toConvertVo(carPo);System.out.println(carVo);

          輸出如下

          CarVo(id=1, brand=BMW)

          可以看到,carPo的屬性值復(fù)制給了carVo。當(dāng)然,在這種情況下,功能和BeanUtils是差不多的,體現(xiàn)不出優(yōu)勢!嗯,我們放在后面說,我們先來說說原理!

          原理

          其實原理就是MapStruct插件會識別我們的接口,生成一個實現(xiàn)類,在實現(xiàn)類中,為我們實現(xiàn)了set邏輯!例如,上面的例子中,給CarCovertBasic接口,生成了一個實現(xiàn)類CarCovertBasicImpl,我們可以用反編譯工具看到源碼如下圖所示

          下面,我們來說說優(yōu)勢

          優(yōu)勢

          (1)兩個類型屬性不一致

          此時CarPo的一個屬性為carName,而CarVo對應(yīng)的屬性為name!

          我們在接口上增加對應(yīng)關(guān)系即可,如下所示

          @Mapperpublic interface CarCovertBasic {CarCovertBasic INSTANCE = Mappers.getMapper(CarCovertBasic.class);
          @Mapping(source = "carName", target = "name")CarVo toConvertVo(CarPo source);}

          測試代碼如下

          CarPo carPo = CarPo.builder().id(1)                       .brand("BMW")                       .carName("寶馬")                       .build();CarVo carVo = CarCovertBasic.INSTANCE.toConvertVo(carPo);System.out.println(carVo);

          輸出如下

          CarVo(id=1, brand=BMW, name=寶馬)

          可以看到carVo已經(jīng)能識別到carPo中的carName屬性,并賦值成功。反編譯的圖如下

          畫外音:如果有多個映射關(guān)系可以用@Mappings注解,嵌套多個@Mapping注解實現(xiàn),后文說明!

          (2)集合類型轉(zhuǎn)換

          如果我們要從List轉(zhuǎn)換為List怎么辦呢?簡單,接口里加一個方法就行

          @Mapperpublic interface CarCovertBasic {    CarCovertBasic INSTANCE = Mappers.getMapper(CarCovertBasic.class);
          @Mapping(source = "carName", target = "name") CarVo toConvertVo(CarPo source);
          List toConvertVos(List source);}

          如代碼所示,我們增加了一個toConvertVos方法即可,mapStruct生成代碼的時候,會幫我們?nèi)パh(huán)調(diào)用toConvertVo方法,給大家看一下反編譯的代碼,就一目了然

          (3)類型不一致

          在CarPo加一個屬性為Date類型的createTime,而在CarVo加一個屬性為String類型的createTime,那么代碼如下

          @Data@Builder@NoArgsConstructor@AllArgsConstructorpublic class CarPo {    private Integer id;    private String brand;    private String carName;    private Date createTime;}
          @Data@Builder@NoArgsConstructor@AllArgsConstructorpublic class CarVo { private Integer id; private String brand; private String name; private String createTime;}

          接口就可以這么寫

          @Mapperpublic interface CarCovertBasic {????CarCovertBasic?INSTANCE?= Mappers.getMapper(CarCovertBasic.class);    @Mappings({        @Mapping(source = "carName", target = "name"),????????@Mapping(target?=?"createTime",?expression?=?"java(com.guduyan.util.DateUtil.dateToStr(source.getCreateTime()))")?????})    CarVo toConvertVo(CarPo source);
          List<CarVo> toConvertVos(List<CarPo> source);}

          這樣在代碼中,就能解決類型不一致的問題!在生成set方法的時候,自動調(diào)用DateUtil類進(jìn)行轉(zhuǎn)換,由于比較簡單,我就不貼反編譯的圖了!

          (4)多對一

          在實際業(yè)務(wù)情況中,我們有時候會遇到將兩個Bean映射為一個Bean的情況,假設(shè)我們此時還有一個類為AtrributePo,我們要將CarPo和AttributePo同時映射為CarBo,我們可以這么寫

          @Data@Builder@NoArgsConstructor@AllArgsConstructorpublic class AttributePo {    private double price;    private String color;}@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic class CarBo {    private Integer id;    private String brand;    private String carName;    private Date createTime;    private double price;    private String color;}

          接口改變?nèi)缦?/p>

          @Mapperpublic interface CarCovertBasic {????CarCovertBasic?INSTANCE?= Mappers.getMapper(CarCovertBasic.class);    @Mappings({        @Mapping(source = "carName", target = "name"),????????@Mapping(target?=?"createTime",?expression?=?"java(com.guduyan.util.DateUtil.dateToStr(source.getCreateTime())")    })    CarVo toConvertVo(CarPo source);
          List toConvertVos(List source);
          CarBo toConvertBo(CarPo source1, AttributePo source2);}

          直接增加接口即可,插件在生成代碼的時候,會幫我們自動組裝,看看下面的反編譯代碼就一目了然。

          (5)其他

          關(guān)于MapStruct還有其他很多的高級功能,我就不一一介紹了。大家可以參考下面的文檔,在用到的時候自行翻閱即可!文檔地址:https://mapstruct.org/documentation/reference-guide/



          原創(chuàng)電子書

          原創(chuàng)思維導(dǎo)圖

          掃碼或微信搜 Java3y?回復(fù)「888」領(lǐng)取1000+原創(chuàng)電子書和思維導(dǎo)圖。

          瀏覽 40
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  加勒比有码 | 国产精品扒开腿做爽爽爽A片唱戏 | 欧美成人一级片 | 在线播放神尾舞视频 | 久久天天夜夜操夜色AV麻豆 |