IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 带你解密:SpringBoot接口到底支持多少种类型的参数? -> 正文阅读

[Java知识库]带你解密:SpringBoot接口到底支持多少种类型的参数?

大家好,我是路人,这是 SpringMVC 系列第 22 篇。

目前所有系列文章已同步至个人博客(itsoku.com),个人博客已改版,更方便翻阅,点击文末左边的阅读原文直达博客。

1、来看 2 个好问题

大家在使用 SpringMVC 或者 SpringBoot 开发接口的时候,有没有思考过下面这 2 个问题

  • 接口的参数到底支持哪些类型?有什么规律可循么?

  • 接口参数的值是从哪里来的呢?

说实话,这 2 个问题非常关键,搞懂原理之后,开发接口将得心应手,今天就带大家从原理上来搞懂这俩问题。

2、SpringMVC 处理请求大概的过程

step1、接受请求

step2、根据请求信息找到能够处理请求的控制器方法

step3、解析请求,组装控制器方法需要的参数的值

step4、通过反射调用送控制器方法

step5、响应结果等

咱们重点来看 step3 参数值组装这个过程。

3、解析处理器方法参数的值

解析参数需要的值,SpringMVC 中专门有个接口来干这个事情,这个接口就是:HandlerMethodArgumentResolver,中文称呼:处理器放放参数解析器,说白了就是解析请求得到 Controller 方法的参数的值。

3.1、处理器方法参数解析器:HandlerMethodArgumentResolver 接口

public?interface?HandlerMethodArgumentResolver?{

?/**
??*?判断当前解析器是否支持解析parameter这种参数
??* parameter:方法参数信息
??*/
?boolean?supportsParameter(MethodParameter?parameter);

?/**
??*?解析参数,得到参数对应的值
??*/
?@Nullable
?Object?resolveArgument(MethodParameter?parameter,?@Nullable?ModelAndViewContainer?mavContainer,
???NativeWebRequest?webRequest,?@Nullable?WebDataBinderFactory?binderFactory)?throws?Exception;

}

3.1、解析参数值的过程

SpringMVC 中会配置多个 HandlerMethodArgumentResolver,组成一个 HandlerMethodArgumentResolver 列表,用这个列表来解析参数得到参数需要的值,相当于 2 嵌套 for 循环,简化版的过程如下:

//1.得到控制器参数列表
List<MethodParameter>?parameterList;
//2.参数解析器列表
List<HandlerMethodArgumentResolver>?handlerMethodArgumentResolverList;
//控制器方法参数
Object[]?handlerMethodArgs?=?new?Object[parameterList.size()];
int?paramIndex?=?0;
//遍历参数列表
for?(MethodParameter?parameter?:?parameterList)?{
????//遍历处理器方法参数解析器列表
????for?(HandlerMethodArgumentResolver?resolver?:?handlerMethodArgumentResolverList)?{
????????if?(resolver.supportsParameter(parameter))?{
????????????handlerMethodArgs[paramIndex++]?=?resolver.resolveArgument(parameter,?webRequest,?binderFactory);
????????????break;
????????}
????}
}

解析参数源码的位置:

org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

4、常见的 HandlerMethodArgumentResolver

大家可以在InvocableHandlerMethod#getMethodArgumentValues这个位置设置断点,可以详细了解参数解析的过程,debug 中我们可以在这看到 SpringMVC 中默认情况下注册了这么多解析器,如下图:

d3d8d2870a591a6418511c2a4f62cf41.png

如下表,列出了一些常见的,以及这些参数解析器能够解析的参数的特点及类型

实现类支持的参数类型参数值
RequestParamMethodArgumentResolver参数需使用@RequestParam 标注,且 name 属性有值,参数通常为普通类型、Map 类型;或 MultipartFile、Part 类型,或 MultipartFile、Part 这两种类型的集合、数组请求参数
RequestParamMapMethodArgumentResolver参数需使用@RequestParam 标注,且 name 属性没有子,参数为 Map 类型;参数的值从 request 的参数中取值,Map 中的 key 对应参数名称,value 对应参数的值请求参数
PathVariableMapMethodArgumentResolver参数需使用@PathVariable 标注,参数通常为普通类型从 url 中取值
RequestHeaderMethodArgumentResolver参数需使用@RequestHeader 标注,参数通常为 Map、MultiValueMap、HttpHeaders 类型请求头
ServletCookieValueMethodArgumentResolver参数需使用@CookieValue 标注,参数为普通类型或者 Cookie 类型cookie
ModelMethodProcessor参数为 Model 类型,控制器中可以调用 model.addAttribute 想模型中放数据,最终这些数据都会通过 request.setAttribute 复制到 request 中来源于 SpringMVC 容器
MapMethodProcessor参数为 Map 类型,值同 ModelMethodProcessor来源于 SpringMVC 容器
ModelAttributeMethodProcessor参数需要使用@ModelAttribute 标注Model.getAttribute
ServletRequestMethodArgumentResolver参数类型为 WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneIdServlet 容器中的 request
ServletResponseMethodArgumentResolver参数类型是 ServletResponse、OutputStream、WriterServlet 容器中的 response
ModelMethodProcessor参数为 org.springframework.ui.Model 类型来源于 SpringMVC 容器
RequestAttributeMethodArgumentResolver参数需使用@RequestAttributerequest.getAttribute
SessionAttributeMethodArgumentResolver参数需使用@SessionAttributesession.getAttribute
ExpressionValueMethodArgumentResolver参数需使用@Value 标注从 Spring 配置中取值
ServletModelAttributeMethodProcessor支持为我们自定义的 javabean 赋值-
RequestResponseBodyMethodProcessor参数需使用@RequestBody 标注http 请求中的 body
HttpEntityMethodProcessor参数类型为 HttpEntity 或 RequestEntity 类型,这两种类型的参数基本上包含了请求的所有参数信息http 请求中的完整信息

实现类比较多,就不一一说了,这里教大家一招,让大家学会如何看每种参数解析器的源码,掌握看源码之后,大家把每个实现类的源码过一下,基本上就知道如何使用了,这里以RequestParamMethodArgumentResolver源码为例来做解读。

5、RequestParamMethodArgumentResolver 源码解读

5.1、supportsParameter 方法:判断支持参数类型

源码如下,挺简单的,大家注意看注释,秒懂

public?boolean?supportsParameter(MethodParameter?parameter)?{
????//判断参数上是否有@RequestParam注解
????if?(parameter.hasParameterAnnotation(RequestParam.class))?{
????????//参数是Map类型
????????if?(Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType()))?{
????????????//@RequestParam注解name必须有值
????????????RequestParam?requestParam?=?parameter.getParameterAnnotation(RequestParam.class);
????????????return?(requestParam?!=?null?&&?StringUtils.hasText(requestParam.name()));
????????}?else?{
????????????return?true;
????????}
????}?else?{
????????//判断参数上是否有@RequestPart注解,有则返回false
????????if?(parameter.hasParameterAnnotation(RequestPart.class))?{
????????????return?false;
????????}
????????parameter?=?parameter.nestedIfOptional();
????????/**
?????????*?参数微信是否为下面这些类型,通常文件上传的时候用这种类型接受参数
?????????*?MultipartFile、Collection<MultipartFile>、List<MultipartFile>、MultipartFile[]
?????????*?Part、Collection<Part>、List<Part>、Part[]
?????????*/
????????if?(MultipartResolutionDelegate.isMultipartArgument(parameter))?{
????????????return?true;
????????}?else?if?(this.useDefaultResolution)?{
????????????//?是否开启了默认解析,useDefaultResolution默认是false
????????????return?BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
????????}?else?{
????????????return?false;
????????}
????}
}

5.2、resolveArgument 方法

resolveArgument 方法最终会调用RequestParamMethodArgumentResolver#resolveName方法,代码如下,如果是文件上传的,就获取的是 MultipartFile 对象,否则就是调用request.getParameterValues从参数中取值

protected?Object?resolveName(String?name,?MethodParameter?parameter,?NativeWebRequest?request)?throws?Exception?{
????HttpServletRequest?servletRequest?=?request.getNativeRequest(HttpServletRequest.class);

????Object?arg?=?null;
????MultipartRequest?multipartRequest?=?request.getNativeRequest(MultipartRequest.class);
????if?(multipartRequest?!=?null)?{
????????List<MultipartFile>?files?=?multipartRequest.getFiles(name);
????????if?(!files.isEmpty())?{
????????????arg?=?(files.size()?==?1???files.get(0)?:?files);
????????}
????}
????if?(arg?==?null)?{
????????String[]?paramValues?=?request.getParameterValues(name);
????????if?(paramValues?!=?null)?{
????????????arg?=?(paramValues.length?==?1???paramValues[0]?:?paramValues);
????????}
????}
????return?arg;
}

5、@RequestParam:取请求中的参数

5.1、简介

@RequestParam 注解我们用到的比较多,被这个注解标注的参数,会从 request 的请求参数中取值,参数值为 request.getParameter("@RequestParam 注解 name 的值")

重点来看下这个类的源码,如下,大家要学会看源码中的注释,Spring 注释写的特别的好,这里给 spring 点个赞,注释中详细说明了其用法,大家注意下面匡红的部分,稍后用一个案例代码让大家了解其他常见几种用法,这个注解的用法掌握了,其他的注解都是雷同的,大家去看起源码以及对应的参数解析器,就会秒懂了。

f5adb44cfe7644f5e5d0cc242d4ff482.png

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public?@interface?RequestParam?{

?/**
??*?对应request中参数名称
??*/
?@AliasFor("name")
?String?value()?default?"";

?/**
??*?同value
??*/
?@AliasFor("value")
?String?name()?default?"";

?/**
??*?请求中是否必须有这个参数
??*/
?boolean?required()?default?true;

?/**
??*?默认值
??*/
?String?defaultValue()?default?ValueConstants.DEFAULT_NONE;

}

5.2、案例

案例代码如下,注意 5 个参数,这 5 个参数反应了@RequestParam所有的的用法,这个接口的参数解析会用到 2 个解析器:RequestParamMethodArgumentResolverRequestParamMapMethodArgumentResolver,大家可以设置断点 debug 一下。

注意最后一个参数的类型是 MultiValueMap,这种类型相当于 Map<String,List<String>>

@RequestMapping("/test1")
@ResponseBody
public?Map<String,?Object>?test1(@RequestParam("name")?String?name,
?????????????????????????????????@RequestParam("age")?int?age,
?????????????????????????????????@RequestParam("p1")?String[]?p1Map,
?????????????????????????????????@RequestParam?Map<String,?String>?requestParams1,
?????????????????????????????????@RequestParam?MultiValueMap?requestParams2)?{?//MultiValueMap相当于Map<String,List<String>>
????Map<String,?Object>?result?=?new?LinkedHashMap<>();
????result.put("name",?name);
????result.put("age",?age);
????result.put("p1Map",?p1Map);
????result.put("requestParams1",?requestParams1);
????result.put("requestParams2",?requestParams2);
????return?result;
}

发送请求

http://localhost:8080/chat17/test1?name=ready&age=35&p1=1&p1=2&p1=3

接口输出

{
?"name":?"ready",
?"age":?35,
?"p1Map":?[
??"1",
??"2",
??"3"
?],
?"requestParams1":?{
??"name":?"ready",
??"age":?"35",
??"p1":?"1"
?},
?"requestParams2":?{
??"name":?[
???"ready"
??],
??"age":?[
???"35"
??],
??"p1":?[
???"1",
???"2",
???"3"
??]
?}
}

7、总结

本文带大家了解了参数解析器HandlerMethodArgumentResolver的作用,掌握这个之后,大家就知道控制器的方法中参数的写法,建议大家下去之后,多翻翻这个接口的实现类,掌握常见的参数的各种用法,这样出问题了,才能够快速定位问题,提升快速解决问题的能力。

8、代码位置及说明

8.1、git 地址

https://gitee.com/javacode2018/springmvc-series

8.2、本文案例代码结构说明

9f84fe8cfd67c06a059ee547ef066b39.png

9、SpringMVC 系列目录

  1. SpringMVC 系列第 1 篇:helloword

  2. SpringMVC 系列第 2 篇:@Controller、@RequestMapping

  3. SpringMVC 系列第 3 篇:异常高效的一款接口测试利器

  4. SpringMVC 系列第 4 篇:controller 常见的接收参数的方式

  5. SpringMVC 系列第 5 篇:@RequestBody 大解密,说点你不知道的

  6. SpringMVC 系列第 6 篇:上传文件的 4 种方式,你都会么?

  7. SpringMVC 系列第 7 篇:SpringMVC 返回视图常见的 5 种方式,你会几种?

  8. SpringMVC 系列第 8 篇:返回 json & 通用返回值设计

  9. SpringMVC 系列第 9 篇:SpringMVC 返回 null 是什么意思?

  10. SpringMVC 系列第 10 篇:异步处理

  11. SpringMVC 系列第 11 篇:集成静态资源

  12. SpringMVC 系列第 12 篇:拦截器

  13. SpringMVC 系列第 13 篇:统一异常处理

  14. SpringMVC 系列第 14 篇:实战篇:通用返回值 & 异常处理设计

  15. SpringMVC 系列第 15 篇:全注解的方式 ?&? 原理解析

  16. SpringMVC 系列第 16 篇:通过源码解析 SpringMVC 处理请求的流程

  17. SpringMVC 系列第 17 篇:源码解析 SpringMVC 容器的启动过程

  18. SpringMVC 系列第 18 篇:强大的 RequestBodyAdvice 解密

  19. SpringMVC 系列第 19 篇:强大的 ResponseBodyAdvice 解密

  20. SpringMVC 系列第 20 篇:RestFull 详解

  21. SpringMVC 系列第 21 篇:接口调用利器 RestTemplate

10、更多系列文章

  1. Spring 高手系列(共 56 篇)

  2. Java 高并发系列(共 34 篇)

  3. MySql 高手系列(共 27 篇)

  4. Maven 高手系列(共 10 篇)

  5. Mybatis 系列(共 12 篇)

  6. 聊聊 db 和缓存一致性常见的实现方式

  7. 接口幂等性这么重要,它是什么?怎么实现?

  8. 泛型,有点难度,会让很多人懵逼,那是因为你没有看这篇文章!

11、最新资料

  1. 尚硅谷 Java 学科全套教程(总 207.77GB)

  2. 2021 最新版 Java 微服务学习线路图 + 视频

  3. 阿里技术大佬整理的《Spring 学习笔记.pdf》

  4. 阿里大佬的《MySQL 学习笔记高清.pdf》

  5. 2021 版 java 高并发常见面试题汇总.pdf

  6. Idea 快捷键大全.pdf

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-11-16 18:41:57  更:2021-11-16 18:42:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:42:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码