<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>

          為什么不推薦使用BeanUtils屬性轉(zhuǎn)換工具,老程序員都不使用!

          共 8446字,需瀏覽 17分鐘

           ·

          2021-05-02 00:17

          上一篇:3600萬中國人在抖音“上清華”

           作者:明明如月學長

          blog.csdn.net/w605283073/article/details/107371462

          1 背景

          之前在專欄中講過“不推薦使用屬性拷貝工具”,推薦直接定義轉(zhuǎn)換類和方法使用 IDEA 插件自動填充 get / set 函數(shù)。

          不推薦的主要理由是:

          有些屬性拷貝工具性能有點差 有些屬性拷貝工具有“BUG” 使用屬性拷貝工具容易存在一些隱患(后面例子會講到)

          2 示例

          首先公司內(nèi)部就遇到過 commons 包的 BeanUtils 進行屬性拷貝性能較差的真實案例,然后該同事?lián)Q成了 Spring 的 BeanUtils 性能好了很多,感興趣大家可以使用性能測試框架或者基準測試框架去對比,這里就不對比了。

          接下來我們看 Spring 的 BeanUtils 的屬性拷貝會存在啥問題:

          import lombok.Data;
          import java.util.List;

          @Data
          public class A {
              private String name;

              private List<Integer> ids;
          }
          @Data
          public class B {
              private String name;

              private List<String> ids;
          }
          import org.springframework.beans.BeanUtils;

          import java.util.Arrays;

          public class BeanUtilDemo {
              public static void main(String[] args) {
                  A first = new A();
                  first.setName("demo");
                  first.setIds(Arrays.asList(123));

                  B second = new B();
                  BeanUtils.copyProperties(first, second);
                  for (String each : second.getIds()) {// 類型轉(zhuǎn)換異常
                      System.out.println(each);
                  }
              }
          }

          大家運行上述示例時,會發(fā)生類型轉(zhuǎn)換異常。

          打斷點可以看到,屬性拷貝之后 B 類型的 second 對象中 ids 仍然為 Integer 類型:

          如果不轉(zhuǎn)換為字符串,直接進行打印,并不會報錯。

          使用CGlib 在不定義Converter 的情況下也會遇到類似問題:

          import org.easymock.cglib.beans.BeanCopier;

          import java.util.Arrays;

          public class BeanUtilDemo {
              public static void main(String[] args) {
                  A first = new A();
                  first.setName("demo");
                  first.setIds(Arrays.asList(123));

                  B second = new B();
                  final BeanCopier beanCopier = BeanCopier.create(A.classB.classfalse);
                  beanCopier.copy(first,second,null);

                  for (String each : second.getIds()) {// 類型轉(zhuǎn)換異常
                      System.out.println(each);
                  }
              }
          }

          同樣,問題在運行時才暴露出來。

          接下來我們看下 mapstruct:

          import org.mapstruct.Mapper;
          import org.mapstruct.factory.Mappers;

          @Mapper
          public interface Converter {
              Converter INSTANCE = Mappers.getMapper(Converter.class);

              aToB(A car);
          }
          import java.util.Arrays;

          public class BeanUtilDemo {
              public static void main(String[] args) {
                  A first = new A();
                  first.setName("demo");
                  first.setIds(Arrays.asList(123));

                  B second = Converter.INSTANCE.aToB(first);
                  for (String each : second.getIds()) {// 正常
                      System.out.println(each);
                  }
              }
          }

          可以成功的將 A 中 List<Integer> 轉(zhuǎn)為 B 中的 List<String> 類型。(搜索公眾號互聯(lián)網(wǎng)架構(gòu)師,回復(fù)“2T”,送你一份Java面試題寶典)

          我們看下編譯生成的 Converter 實現(xiàn)類:

          import java.util.ArrayList;
          import java.util.List;
          import javax.annotation.Generated;
          import org.springframework.stereotype.Component;

          @Generated(
              value = "org.mapstruct.ap.MappingProcessor",
              comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_202 (Oracle Corporation)"
          )
          @Component
          public class ConverterImpl implements Converter {

              @Override
              public B aToB(A car) {
                  if ( car == null ) {
                      return null;
                  }

                  B b = new B();

                  b.setName( car.getName() );
                  b.setIds( integerListToStringList( car.getIds() ) );

                  return b;
              }

              protected List<String> integerListToStringList(List<Integer> list) {
                  if ( list == null ) {
                      return null;
                  }

                  List<String> list1 = new ArrayList<String>( list.size() );
                  for ( Integer integer : list ) {
                      list1.add( String.valueOf( integer ) );
                  }

                  return list1;
              }
          }

          自動幫我們進行了轉(zhuǎn)換,我們可能沒有意識到類型并不一致。

          如果我們在 A 類中添加一個 String number 屬性,在 B 類中添加一個 Long number 屬性,使用 mapstruect 當 number 設(shè)置為非數(shù)字類型時就會報 .NumberFormatException

          @Override
          public B aToB(A car) {
              if ( car == null ) {
                  return null;
              }

              B b = new B();

              b.setName( car.getName() );
              if ( car.getNumber() != null ) { // 問題出在這里
                  b.setNumber( Long.parseLong( car.getNumber() ) );
              }
              b.setIds( integerListToStringList( car.getIds() ) );

              return b;
          }

          使用 cglib 默認則不會映射 number 屬性,B 中的 number 為 null。

          如果手動定義轉(zhuǎn)換器,使用 IDEA 插件(如 generateO2O)自動轉(zhuǎn)換:

          public final class A2BConverter {

              public static B from(A first) {
                  B b = new B();
                  b.setName(first.getName());
                  b.setIds(first.getIds());
                  return b;
              }
          }

          在編碼階段就可以非常明確地發(fā)現(xiàn)這個問題:

          3 結(jié)論

          由于 Java 的泛型其實是編譯期檢查,編譯后泛型擦除,導(dǎo)致運行時 List<Integer> 和 List<String> 都是 List 類型,可以正常賦值。這就導(dǎo)致在使用很多屬性映射工具時,編譯時不容易明顯的錯誤。

          mapstruct 自定義了注解處理器,在編譯階段可以讀取映射雙方的泛型類型,進而進行映射。但是這種映射也很可怕,有時候我們由于粗心等原因定義錯了類型,自動幫助我們進行了轉(zhuǎn)換,會帶了很多副作用。

          之前對各種屬性映射工具的性能進行了簡單的對比,結(jié)果如下:

          因此慎用屬性轉(zhuǎn)換工具,如果可能建議自定義轉(zhuǎn)換類,使用IDEA插件自動填充,效率也挺高, A 或 B 中任何屬性類型不匹配,甚至刪除一個屬性,編譯階段即可報錯,而且直接調(diào)用 get set 的效率也是非常高的。


          看完這篇文章,你有什么收獲?歡迎在留言區(qū)與10w+Java開發(fā)者一起討論~

          關(guān)注微信公眾號:互聯(lián)網(wǎng)架構(gòu)師,在后臺回復(fù):2T,可以獲取我整理的教程,都是干貨。


          猜你喜歡

          1、GitHub 標星 3.2w!史上最全技術(shù)人員面試手冊!FackBoo發(fā)起和總結(jié)

          2、如何才能成為優(yōu)秀的架構(gòu)師?

          3、從零開始搭建創(chuàng)業(yè)公司后臺技術(shù)棧

          4、程序員一般可以從什么平臺接私活?

          5、37歲程序員被裁,120天沒找到工作,無奈去小公司,結(jié)果懵了...

          6、滴滴業(yè)務(wù)中臺構(gòu)建實踐,首次曝光

          7、不認命,從10年流水線工人,到谷歌上班的程序媛,一位湖南妹子的勵志故事

          8、15張圖看懂瞎忙和高效的區(qū)別

          9、2T架構(gòu)師學習資料干貨分享


          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  激情黄色免费视频 | 自拍视频在线网址 | 免费jlzzjlzz在线播放欧美 | 国产精品人人妻人人爽人人牛 | 国产精品 视频瘾无码 |