日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

map insert异常失败_处理dubbo反序列化失败的坑

發布時間:2024/7/23 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 map insert异常失败_处理dubbo反序列化失败的坑 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

??今天下午,當我經過一個小時的奮”鍵“疾”碼“,準備好好的審查一下(摸魚)自己寫的代碼,經過一段時間審查(摸的差不多了,該下班了),得出一個結論我寫的代碼很優雅、精簡。所以大手一揮提交代碼,并在 API 管理系統上將 xxx 接口點了個完成。準備收拾東西走人了準點下班。然而事與愿違,沒過多久前端大哥就@我了,說 xxx 接口有問題,麻煩處理一下。內心第一反應(你丫的參數傳錯了吧)卑微的我只能默默的回個,好的、麻煩把參數給我一下,我這邊檢查一下[微笑臉]。

場景還原

??經過測試,發現確實是我的問題。還好沒甩鍋,要不然就要被打臉了。錯誤信息如下:

{
??"code":?"010000",
??"message":"java.util.HashMap?cannot?be?cast?to?com.aixiao.inv.common.dto.tax.AddEmployeeDTO$Employee",
??"data":?null
}

??看到這個錯誤有點懵,HashMap 無法轉換為AddEmployeeDTO$Employee。內心在想,沒道理啊。請求參數我都是拷貝過來的,壓根就沒用Map進行參數傳遞。畢竟我都是個老手了,咋可能犯這樣愚蠢的錯誤。俗話說遇到問題不要慌,讓我們掏出手機先發個朋友圈,不對好像有點跑題了,我們先看一下調用鏈的數據傳遞。

??首先 web 將AddEmployeeForm數據傳遞到服務端,然后使用fromToDTO()方法,進行將數據轉換為 Dubbo 請求需要的AddEmployeeDTO。Dubbo 服務放接收AddEmployeeDTO后,使用 EmployeeConvert 將數據轉換為AddEmployeeXmlReq再執行相關邏輯。

AddEmployeeForm 類

@Data
public?class?AddEmployeeForm?implements?Serializable?{

????/**
?????*?職員信息列表
?????*/
????private?List?employees;@Datapublic?static?class?Employee?implements?Serializable?{/**
?????????*?姓名
?????????*/private?String?name;/**
?????????*?工作
?????????*/private?String?job;
????}
}

FormToDTO()方法

public??T?formToDTO(F?form,?T?dto)?{//?進行數據拷貝
????BeanUtils.copyProperties(form,?dto);//?返回數據return?dto;
}

AddEmployeeDTO 類

@Data
public?class?AddEmployeeDTO?implements?Serializable?{

????/**
?????*?職員信息列表
?????*/
????private?List?employees;@Datapublic?static?class?Employee?implements?Serializable?{/**
?????????*?姓名
?????????*/private?String?name;/**
?????????*?工作
?????????*/private?String?job;
????}
}

EmployeeConvert 轉換類

?

EmployeeConvert 轉換類,使用了mapstruct進行實現,沒使用過的小伙伴可以簡單的了解下。

?@Mapper
public?interface?EmployeeConvert?{

????EmployeeConvert?INSTANCE?=?Mappers.getMapper(EmployeeConvert.class);

????AddEmployeeXmlReq?dtoToXmlReq(AddEmployeeDTO?dto);

}

AddEmployeeXmlReq 類

@Data
public?class?AddEmployeeXmlReq?implements?Serializable?{

????/**
?????*?職員信息列表
?????*/
????private?List?employees;@Datapublic?static?class?Employee?implements?Serializable?{/**
?????????*?姓名
?????????*/private?String?name;/**
?????????*?工作
?????????*/private?String?job;
????}
}

EmployeeController

@RestController
@AllArgsConstructor
public?class?EmployeeController?{

????private?final?EmployeeRpcProvider?provider;

????@PostMapping("/employee/add")
????public?ResultVO?employeeAdd(@RequestBody?AddEmployeeForm?form)?{
????????provider.add(formToDTO(form,new?AddEmployeeDTO()));
????????return?ResultUtil.success();
????}
}

EmployeeRpcServiceImpl

@Slf4j
@Service
public?class?EmployeeRpcServiceImpl?implements?EmployeeService?{

????@Override
????public?ResultDTO?add(AddEmployeeDTO?dto)?{
????????log.info("dubbo-provider-AddEmployeeDTO:{}",?JSON.toJSONString(dto));
????????AddEmployeeXmlReq?addEmployeeXmlReq?=?EmployeeConvert.INSTANCE.dtoToXmlReq(dto);
????????return?ResultUtil.success();
????}
}

分析原因

判斷異常拋出點

??我們需要先確定異常是在consumer 拋出的還是provider拋出的。判斷過程很簡單,我們可以進行本地debug,看看是執行到哪里失敗了就知道了。如果不方便本地調試,我們可以在關鍵點上打上相應的日志。比如說consumer調用前后,provider處理前后。如果請求正常 日志打印的順序應該是:

這樣通過觀察日志就可以判定異常是在哪里拋出的了。

?

實際并沒有這樣麻煩,因為在 consumer 做了 rpc 異常攔截,所以我當時看了下 consumer 的日志就知道是 provider 拋出來的。

?

找到出錯的代碼

??既然找到了出問題是出在provider,那看是什么原因導致的,從前面的調用鏈可以知道,provider接收到AddEmployeeDTO會使用EmployeeConvert將其轉換為AddEmployeeXmlReq,所以我們可以打印出AddEmployeeDTO看看consumer的傳參是否正常。

??通過日志我們可以發現consumer將參數正常的傳遞過來了。那么問題應該就出在EmployeeConvert將AddEmployeeDTO轉換為AddEmployeeXmlReq這里了。由于EmployeeConvert是使用mapstruct進行實現,我們可以看看自動生成的轉換類實現邏輯是咋樣的。

??通過觀察源代碼可以發現,在進行轉換的時候需要傳入一個List 而這個Employee正是AddEmployeeDTO.Employee。這個時候可能會困擾了,我明明就是傳入AddEmployeeDTO,而且類里面壓根就沒有Map,為啥會拋出java.util.HashMap cannot be cast to com.aixiao.inv.common.dto.tax.AddEmployeeDTO$Employee這個異常呢?

讓我們Debug一下看看發生了啥。

??這個時候你會發現接收到的AddEmployeeDTO.employees內存儲的并不是一個AddEmployeeDTO$Employee對象,而是一個HashMap。那看來真相大白了,原來是 dubbo 反序列化的時候將AddEmployeeDTO$Employee 轉換為HashMap了。從而導致了java.util.HashMap cannot be cast to com.aixiao.inv.common.dto.tax.AddEmployeeDTO$Employee異常的拋出。

你以為結束了?

??為啥Dubbo反序列化時會將AddEmployeeDTO$Employee變成Map呢?我們回過頭看看之前打印參數的日志,有一個警告日志提示了java.lang.ClassNotFoundException:com.aixiao.inv.api.model.form.AddEmployeeForm$Employee ,找不到AddEmployeeForm$Employee這個就有點奇怪了,為啥不是AddEmployeeDTO$Employee?

??在進行dubbo調用前AddEmployeeForm會使用fromToDTO()方法將其轉化為AddEmployeeDTO。那么問題會不會出現在這里呢?我們繼續Debug看看。

??嘔吼,這下石錘了。原來是在formToDTO的時候出問題了。傳遞過去AddEmployeeDTO內部的Employee竟然變成了AddEmployeeForm$Employee。這也是為什么provider那邊會拋出java.lang.ClassNotFoundException:com.aixiao.inv.api.model.form.AddEmployeeForm$Employee的原因了。審查一下formToDTO的代碼看看為啥會發生這樣的情況:

public??T?formToDTO(F?form,?T?dto)?{//?進行數據拷貝
????BeanUtils.copyProperties(form,?dto);//?返回數據return?dto;
}

??fromToDTO內的代碼非常精簡,就一個BeanUtils.copyProperties()的方法,那毫無疑問它就是罪魁禍首了。通過在 baidu 的海洋里遨游,我找到了原因。原來是BeanUtils是淺拷貝造成的。淺拷貝只是調用子對象的 set 方法,并沒有將所有屬性拷貝。(也就是說,引用的一個內存地址),所以在轉換的時候,將AddEmployeeDTO內的employees屬性指向了AddEmployeeForm的employees的內存地址。所以將在進行調用時,Dubbo因為反序列化時找不到對應的類,就會將其轉換為Map。

小結一下

??上面的問題,主要是由于 BeanUtils 淺拷貝造成。并且引發連鎖反應,造成Dubbo反序列化異常以及EmployeeConvert的轉換異常,最后拋出了java.util.HashMap cannot be cast to com.aixiao.inv.common.dto.tax.AddEmployeeDTO$Employee 錯誤信息。

解決方法

??既然知道了問題出現的原因,那么解決起來就很簡單了。對于單一的屬性,那么不涉及到深拷貝的問題,適合用 BeanUtils 繼續進行拷貝。但是涉及到集合我們可以這樣處理:

  • 簡單粗暴使用 foreach 進行拷貝。

  • 使用 labmda 實現進行轉換。

  • AddEmployeeDTO?dto?=?new?AddEmployeeDTO();
    dto.setEmployees(form.getEmployees().stream().map(tmp?->?{
    ??AddEmployeeDTO.Employee?employee?=?new?AddEmployeeDTO.Employee();
    ??BeanUtils.copyProperties(tmp,employee);
    ??return?employee;
    }).collect(Collectors.toList()));
  • 封裝一個轉換類進行轉換。
  • AddEmployeeDTO?dto?=?new?AddEmployeeDTO();
    dto.setEmployees(convertList(form.getEmployees(),AddEmployeeDTO.Employee.class));

    public??List?convertList(List?source,?Class?targetClass)?{return?JSON.parseArray(JSON.toJSONString(source),?targetClass);
    }

    總結

  • 使用 BeanUtils.copyProperties()進行拷貝需要注意
  • dubbo 在進行反序列化的時候,如果找不到對應類會將其轉化為 map。
  • 參考

    • BeanUtils.copyProperties 的使用(深拷貝,淺拷貝)

    結尾

    ??我是不一樣的科技宅,每天進步一點點,體驗不一樣的生活。我們下期見!

    ??如果覺得對你有幫助,可以多多評論,多多點贊哦,也可以到我的主頁看看,說不定有你喜歡的文章,也可以隨手點個關注哦,謝謝。

    總結

    以上是生活随笔為你收集整理的map insert异常失败_处理dubbo反序列化失败的坑的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。