查看原文
其他

谁再用 Map 传参,上去就给他一jio,别客气

一航 一行Java 2023-02-06

大家好,我是一航;

前几天,技术群(文末可加)里面有小伙伴儿在交流ApiPost在请求数据是List的时候,有个bug;为了验证,然后在Controller分别写了List和Map接收请求参数的接口进行测试;

也是出于好奇,就在群里面问了一句:有多少人工作中写接口也用Map来接收数据的?结果发现还有好些个伙伴说现在就用的Map,让我还挺惊讶的,问其原因,出奇一致的说:所有请求格式都可以;换个表达就是:啥都能往里面塞......

好吧!我承认,这确实算是个优点;

但是,在我的团队里面,是明令禁止,不允许使用Map来进行传参的(除了那些和业务无关的工具类);这个传参,不限于接口参数,业务逻辑间的调用也是一样;那为什么要这么规定呢?

下面就来好好交流一下使用Map传参存在那些优缺点;概括如下:

  • 优点

    • 灵活(才疏学浅,作为传参,目前我只能总结这么一个了)
  • 缺点

    • 代码可读性差
    • 使用麻烦
    • 大量硬代码
    • 可维护性差
    • 校验参数麻烦
    • 文档工具不友好

总结起来就一句话:优点没啥,缺点一箩筐

下面就来详细说说:

优点

灵活

唯一一个我觉得好的地方,就是足够灵活了;

使用Map<String,Object>去接收请求传参时,不管是URL链接上的keyvalue值,还是通过Body传递的Json串,都能直接转换成Map,不管是有多复杂的Json串,都能解析映射成Map;不需要专门定义任何额外的对象来接收数据;

如果要添加参数,只需要在Url或者Json数据中加上键值对,后端接口的Map中就能接收到新添加的值了;而且数据格式不限,因为Value是Object类型的,所以基础类型的任意值、文本,都是可以接收到的。

示例代码:

@PostMapping
public void add(@RequestBody Map<String, Object> userInfo) {
    log.info("userInfo:{}", userInfo);
}

请求测试

接口地址:http://127.0.0.1:8080/userInfo      

body数据:

{

"userName":"张三",

"age":10,

"email":"xxxx@qq.com"

}

后端接收到的数据如下:

如果单就说到这里,是不是觉得还挺不错的,啥也没写,所有数据都收到了,也挺好的呀!

但是....;这仅仅只是个开始,坑在后面呢!

另外;这个所谓的灵活,换个说法就是:没有约束、没有规则;这里虽说是优点,但同时也衍生出很多问题,既然没有约束,那就是任何人在任何时间都可以往里面放任意值;一旦文档更新不及时,会造成系统维护一段时间之后,谁都不清楚这个Map里面具体的结构是什么样的了。

缺点

代码可读性差

在和大家交流的过程中,我说过一个观点:源代码目的不是用来执行的,而是用来阅读的;让我们设身处地的想一下,某天你接手别人的一个项目,想了解一下业务逻辑,打开一个Controller,看到的是一个个的Map传参,你会是一种什么样的心情;我猜一定是想骂娘;因为这样的话,你想要了解这个接口的数据交互,就必须把这个接口后续的所有业务逻辑都过一遍,看看代码中取了那些值,添加了什么;不然单靠接口的定义,完全无法知道,交互传递了那些参数;

如下图,只有debug调试,点开+号的那一瞬间,才能知道前端传了什么;

而且,传递的这些参数是否符合规范,也无从知晓,只能去查文档;

另外,很多RPC都是基于接口开发,你根本无法看到服务端的代码;如果给你提供的RPC服务接口中采用的是Map传参,那就意味着,你在没有拿到详细的文档之前,是根本无法去做客户端调用的;

使用麻烦

存取值异常的麻烦,不管是客户端调用之前,还是服务端接收之后;赋值和取值的过程非常繁琐;需要预先知道传参的Key以及数据类型,才能拿到有效的数据;

如下示例,每个参数都需要知道他具体的key值以及类型;这还只是单程的获取,一旦涉及到嵌套对象,取值就更麻烦了;

大量硬代码

存值、赋值的过程,充斥着大量关于key的硬代码;而且只要Map在,整条业务线上这样的操作随处可见;而这种代码,看一眼就不想再多瞄第二眼了。

可维护性差

  • Map

    技术方案总是会随着进度的推进,发生一些细微的变化;比如用户的用户名,一开始定义的时候是使用的name作为Key进行传输,开发的过程中,觉得name表达的不够准确,想换成userName;

    当你使用Map接收的时候,你需要自行去查找,到底有那些地方使用的map.get("name"),然后把他们都换成map.get("userName");一旦查找替换遗漏,就可能带来业务上的Bug;

  • 对象

    当如果你使用对象的接收传参时,情况就不一样了;你只需要将对象中的名称name更换为userName并重新生成get、set方法,并将之前所有使用过get、set的地方做相应的调整即可;而且,当你修改之后,开发工具能立马感知修改并提醒你,只能全部修改完才能正常运行

校验参数麻烦

API做参数的合法性校验是很重要的一项工作,他能大大降低后续业务逻辑代码出错率;

  • Map

    当使用Map传参时,需要校验参数的合法性就变的非常的麻烦;比如就一个简单的判空,你需要先在Map中将get出来,然后对取出来的值进行if判空校验;没办法使用一些优秀的开源工具来提高开发效率;

    这样不仅要写大量的苦力代码,还让代码变的冗长,大大降低了代码的可读性;

  • 对象

    当使用对象来接收时,就可以轻松使用Validate来做各种优雅的参数校验

    详细的Validata使用,可参考之前的一篇文章:SpringBoot!你的请求、响应、异常规范了吗?

    下图就是两种不同方式的校验,谁更优雅相信一目了然了

文档工具不友好

比如当你使用Swagger自动生成API接口文档时,如果你定义的是对象,那么生成的文档会自动将对象中的各个属性在生成的文档中展示出来;客户端能通过文档,很明确的知道最终交互需要传递的字段;

但如果你使用的Map,自动生成的文档接口就无法生成各参数的说明,因为框架只知道你要传递一个Map对象,具体有那些详细属性就不得而知了;

对象实体多、乱的问题

讨论的过程,还有人提到另一个问题,当所有的交互用对象传输,那岂不是要创建很多不同的对象;这同样也是体力活、还让项目变的更乱;

当不使用Map,实体类变多,这个是必然的;如果你不好好的管理,确实会造成乱的问题,但是这些问题,只要稍微用点点心思,就不会成为问题了;

  • 对象创建的体力活

    很多交互DTO对象对象和数据库PO对象结构是相差无几的,所以必要的时候可以使出CV大法;并做一些简单的调整,就可以直接使用了;

  • Json转对象

    安装JsonFormatPlus插件,就能轻松将Json格式的数据转换成实体对象;

  • 完善项目结构

    实体多导致项目乱,更多是因为目录不清晰、对象命名不规范导致的,正确的做法应该是对各个模块、各个功能详细的分组、分类;这样,同一个文件夹下的文件就只有更当前功能有关的类,这样也就不会有错乱了;

  • 合理的复用对象

    比如用户的交互对象,添加功能和修改功能除了对象中id属性不一样之外,其他都一样;那么完全就可以复用同一个对象;Validata校验的时候,合理使用group对参数进行分组校验;这样就可以实现多个功能,使用同一个实体类;

    响应数据也是同理,可以采用@JsonView来实现服用

    详细的使用,可参考:SpringBoot!你的请求、响应、异常规范了吗?

总结

本文并没有任何针对Map,贬低Map的意思;Map在实际的开发过程中,对我们的帮助是很大的;只是作为传参对象时,存在这诸多的问题,所以不太建议使用;因为那点微弱的优势,无法弥补其带来的问题;

所以如果你现在的代码中还在使用Map传参的话,为了避免挨揍,还是早点换了吧!

今天的分享就到这里;既然都看完了,留下你的三连再走可好!(手动狗头)

END

精品资料,超赞福利,免费领


微信扫码/长按识别 添加【技术交流群
已整理2T+视频资料,12G+电子书
群内每天分享精品学习资料


最近开发整理了一个用于速刷面试题的小程序;其中收录了上千道常见面试题及答案(包含基础、并发、JVM、MySQL、Redis、Spring、SpringMVC、SpringBoot、SpringCloud、消息队列等多个类型),欢迎您的使用。


三款神器,让生产力炸裂!一键生成,直接调用
【原创】怒肝3W字Java学习路线!从入门到封神全包了
妙用Java 8中的 Function接口;轻松消灭if...else
8 款在线 API 接口文档管理工具;好用!
用 Long 做 Map 的 Key,存的对象花一下午才取出来,坑惨了!
是时候丢掉 Postman、Swagger 了;这个工具全部搞定,真香!

👇👇
👇点击"阅读原文",获取更多资料(持续更新中)

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存