天天看點

關于在項目中使用 ModelMapper 的利弊

一、背景

在項目開發時,前期推薦我們使用 ModelMapper 對 DO-DTO-VO 進行轉換,沒 PO,我們也從中嘗到了甜頭,因為總體代碼量少,映射有問題就會提示;但是到了中後期,發現 ModelMapper 轉換産生的 bug 不易排查,甚至産生了未知原因的 bug。為啥說是未知呢,首先我們的服務是微服務,每個服務都是多執行個體的保證高可用,但是在其中一個執行個體上 ModelMapper 轉換對象時就報錯,其他執行個體正常。

二、如何使用 ModelMapper

關于如何使用 ModelMapper 可以參考我寫的一篇部落格:ModelMapper 的進階使用 ,也可以百度,可以百度出一堆出來。

三、ModelMapper 之利弊

ModelMapper 之是以能夠在 Domain Driven Design 占據一席之地,說明是有其他工具沒有的優點。但在說優點前先說說 ModelMapper 的缺點:

  • ModelMapper 使用的是反射原理,在性能上肯定沒有 get/set 快(MapStruct、BeanUtils等),MapStruct 本質也是用的 get/set;
  • 第二個缺點是 bug 排查難,主要由于某些字段在轉換前後引起值的變化,對應新手排查問題是一個困難的事和難以發現的事;
  • 學習成本相對較高。

ModelMapper 的優點:

  • 對于 DO-DTO-VO 轉換中屬性一緻的對象轉換基本一行代碼搞定;
  • 能夠快速修正遺漏字段,保證資料一緻性,運用場景有:資料清洗、資料同步;
  • 可在轉換時增加自定義邏輯;

四、感受

我先直接上代碼:

BeanUtils.copyProperties(userInfo, user);
           

對于上述代碼,我想大家都能看懂,将 userInfo 複制到 user ,但是有沒有想過,如果 userInfo 中字段名和 user 中字段名不一緻會怎麼樣,如果一緻但類型不一緻又會怎麼樣,我在這說一下,會導緻 user 中獨有的字段值為 null,程式還不會報錯。對于粗心的人來說,這絕對是個大 bug,但如果用 ModelMapper 就不會産生這種問題,為啥呢?因為 ModelMapper 會檢查,會提示你,在你啟動項目的時候直截了當的甩個報錯資訊給你,類似于下面的報錯資訊

1) Unmapped destination properties found in TypeMap[UserInfo-> setDel]:
	com.dao.entity.UserInfo.setDel()
1 error
           

這時你就知道了吧,你這轉換時映射有問題,你快去看一下。當然,除了上面能夠将映射錯誤的資訊提示你之外,還可以這樣:

modelMapper.createTypeMap(UpdateUserInfoRequestVO.class, UpdateUserInfoDTO.class)
                .addMappings(mapper -> mapper.using((Converter<List<String>, String>) context -> {
                    if (CollectionUtils.isEmpty(context.getSource())) {
                        return "";
                    }
                    return String.join(";", context.getSource());
                }).map(UpdateUserInfoRequestVO::getCaseCharacter, UpdateUserInfoDTO::setCaseNature));
           

這樣:

modelMapper.createTypeMap(UpdateUserInfoRequestVO.class, UpdateUserInfoDTO.class)
                .addMappings(new PropertyMap<UpdateUserInfoRequestVO, UpdateUserInfoDTO>() {

                    @Override
                    protected void configure() {
                        this.using(ctx -> {
                            UpdateUserInfoRequestVOsource = (UpdateUserInfoRequestVO) ctx.getSource();
                            if (source == null) {
                                return null;
                            }
                            List<String> applyPersons = source.getApplyPersonIdList();
                            if (CollectionUtils.isEmpty(applyPersons)) {
                                return null;
                            }
                            return String.join(",", applyPersons);
                        }).map(source).setApplyPersonId(null);
                    }
                });
           

通過在轉換是增加邏輯,而減少在業務處理的 Service 中增加這樣的邏輯。

綜上,我建議對于老手來說可以考慮使用 ModelMapper ,而對于新手來說是不建議的,另外,在生産環境不建議使用 ModelMapper,因為萬一發生一個未知錯誤,就gg了,到時技術經理問你為什麼,别到時你隻會說“我本地測試沒有問題,在測試環境測試也沒有問題的”。