【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

02-27 1073阅读 0评论

文章目录

  • 一、前言
  • 二、正文
    • 1、参数处理器HandlerMethodArgumentResolver的加载
      • 1)《门面》HandlerMethodArgumentResolverComposite
      • 2)参数解析器HandlerMethodArgumentResolver的分类
      • 2、参数处理器的执行时机
        • 参数解析器的选择
        • 3、处理注解类型的参数
          • 1)处理@RequestParam标注的参数
            • 解析参数
              • createNamedValueInfo()
              • resolveName()
              • 2)处理@PathVariable标注的参数
                • 1> supportsParameter()方法实现
                • 2> createNamedValueInfo()
                • 3> resolveName()
                • 3)处理@RequestBody标注的参数
                  • 1> supportsParameter()
                  • 2> 参数解析
                    • 匹配参数对应的HttpMessageConverter
                    • HttpMessageConverter将请求数据读取到JavaType
                    • HttpMessageConverter的来源?
                    • 4、非基础类型参数
                      • 1)处理Request类型的参数(比如:HttpServletRequest)
                        • 1> 支持处理的参数类型
                        • 2> 参数解析
                        • 2)Map类型
                        • 3)Model类型
                        • 5、基础类型参数
                        • 三、总结

                          一、前言

                          Spring MVC源码分析相关文章已出:

                          【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型),【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型),词库加载错误:未能找到文件“C:\Users\Administrator\Desktop\火车头9.8破解版\Configuration\Dict_Stopwords.txt”。,使用,我们,接口,第1张
                          (图片来源网络,侵删)
                          1. Spring MVC <Form>表单中支持REST风格DELETE、PUT类型方法的方式和原理
                          2. Spring MVC请求执行流程
                          3. Spring MVC如何将请求映射到Controller
                          4. 使用FastJsonHttpMessageConverter解析@RequestBody参数

                          更多Spring系列源码分析文章见SpringBoot专栏:

                          • 精通Spring Boot

                            当前文章Spring版本:5.2.12.RELEASE,不同版本类名可能不同,但核心思想都是一样的。

                            二、正文

                            从SpringMVC的执行流程(参考博文:Spring MVC请求执行流程)来看,RequestMappingHandlerAdapter这个HandlerAdapter负责执行Controller中的相应方法,并对请求参数进行处理。

                            1、参数处理器HandlerMethodArgumentResolver的加载

                            RequestMappingHandlerAdapter实现了InitializingBean接口,因此在SpringBoot启动过程中实例化RequestMappingHandlerAdapter过程中(执行init-method()方法之前),会调用RequestMappingHandlerAdapter#afterPropertiesSet()方法:

                            @Override
                            public void afterPropertiesSet() {
                            	// Do this first, it may add ResponseBody advice beans
                            	initControllerAdviceCache();
                            	if (this.argumentResolvers == null) {
                            		List resolvers = getDefaultArgumentResolvers();
                            		this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
                            	}
                            	if (this.initBinderArgumentResolvers == null) {
                            		List resolvers = getDefaultInitBinderArgumentResolvers();
                            		this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
                            	}
                            	if (this.returnValueHandlers == null) {
                            		List handlers = getDefaultReturnValueHandlers();
                            		this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
                            	}
                            }
                            

                            1)《门面》HandlerMethodArgumentResolverComposite

                            【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                            RequestMappingHandlerAdapter中的参数解析器变量argumentResolvers的类型是 HandlerMethodArgumentResolverComposite;HandlerMethodArgumentResolverComposite又组合了所有的HandlerMethodArgumentResolver;

                            【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型),【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型),词库加载错误:未能找到文件“C:\Users\Administrator\Desktop\火车头9.8破解版\Configuration\Dict_Stopwords.txt”。,使用,我们,接口,第3张
                            (图片来源网络,侵删)

                            【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                            因此可以将HandlerMethodArgumentResolverComposite看做是所有参数解析器的“门面”,但凡需要用到参数解析器都都将请求先打到HandlerMethodArgumentResolverComposite。

                            • 这个设计和SpringBoot的事件监听器EventPublishingRunListener 异曲同工。

                              【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                              【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                              getDefaultArgumentResolvers()方法获取所有的参数解析器HandlerMethodArgumentResolver时就是单纯的new,然后放到List中。

                              • 我们自定义了参数解析器时,同样也会被加载到。

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型),【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型),词库加载错误:未能找到文件“C:\Users\Administrator\Desktop\火车头9.8破解版\Configuration\Dict_Stopwords.txt”。,使用,我们,接口,第8张
                                (图片来源网络,侵删)

                                获取到所有的HandlerMethodArgumentResolver之后,直接new一个HandlerMethodArgumentResolverComposite,然后将所有的HandlerMethodArgumentResolver都添加到HandlerMethodArgumentResolverComposite中。

                                2)参数解析器HandlerMethodArgumentResolver的分类

                                从RequestMappingHandlerAdapter#getDefaultArgumentResolvers()方法获取所有的参数解析器HandlerMethodArgumentResolver中注释来看,Spring MVC中将参数解析器分为四大类:

                                1. 基于注解 解析参数,比如:接口方法参数中的@RequestParam、@PathVariable、@RequestBody等注解。
                                2. 基于类型 解析参数,比如:接口方法参数是HttpServletRequest、HttpServletResponse等。
                                3. 自定义参数解析,比如:接口方法参数上有自定义注解,则可在实例化RequestMappingHandlerAdapter时 将自定义的HandlerMethodArgumentResolver接口实现类设置到 customArgumentResolvers 属性中。
                                4. 兜底的参数解析,当上述三类参数解析器都无法解析参数时,此类参数解析器会解析。比如最简单的GET请求:http://localhost:8080/trace/test?name=saint

                                  【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                参数解析器的应用顺序实际就是越晚添加到List集合中,就越最后被遍历到。

                                日常开发过程中一般不需要自定义参数解析器,就可以覆盖90%以上的开发需求。下一篇文章输出自定义参数解析器。

                                2、参数处理器的执行时机

                                我们知道请求是交给HandlerAdapter执行的,对于普通的HTTP请求也就是交给RequestMappingHandlerAdapter执行,RequestMappingHandlerAdapter要执行某个方法时,会将RequestMappingHandlerAdapter中的参数解析器“门面”绑定给方法。

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                继续往下跟,到真正解析前的代码路径如下:

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                InvocableHandlerMethod#getMethodArgumentValues()方法会应用相应的参数解析器从请求中解析出方法的参数值。

                                根据方法参数标注的注解、参数类型会选择不同的参数解析器HandlerMethodArgumentResolver。有种策略模式的意思。

                                参数解析器的选择

                                选择参数解析器HandlerMethodArgumentResolver时,有一层小优化,针对每个方法的参数对应的参数解析器都会做一个缓存。如果从缓存中找不到方法参数对应的参数解析器,则遍历所有的参数解析器(默认26个),调用参数解析器的supportsParameter()方法判断参数解析器是否支持当前参数的解析,如果支持则直接返回。

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                下面以不同的入参形式暂开具体的讨论。

                                3、处理注解类型的参数

                                1)处理@RequestParam标注的参数

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                @RequestParam常用于GET请求,注解里的value或name互为别名、对应请求的参数名,required()属性表示参数是否必须存在。

                                分析以如下请求为例:

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                这里特意将方法的参数名和@RequestParam中的value设置为不一样的值。

                                RequestParamMethodArgumentResolver负责解析被@RequestParam标注的参数。

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                该实现类仅支持处理被@RequestParam注解标注的参数(参数如果是Map类型,@RequestParam注解里的name/value必须有值,否则不知道应该拿哪个KV键值对)。

                                如果参数没有被@RequestParam注解标注,走进的逻辑是兜底的参数解析逻辑。

                                解析参数

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                解析参数会走入到HandlerMethodArgumentResolver#resolveArgument()方法;

                                Spring MVC设计了一个抽象父类AbstractNamedValueMethodArgumentResolver,用于给参数解析器提供一种通用的模板实现,子类通过重写模板中的某个方法实现自己的逻辑。

                                RequestParamMethodArgumentResolver亦是AbstractNamedValueMethodArgumentResolver的子类,所以执行RequestParamMethodArgumentResolver#resolveArgument()实际会进入到:

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                @Override
                                @Nullable
                                public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                                		NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
                                    // 1. 根据方法入参获取参数的配置信息(针对@RequestParam为:拿到参数的@RequestParam注解属性)
                                	NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
                                	MethodParameter nestedParameter = parameter.nestedIfOptional();
                                    // 2. 根据上面参数配置的name 获取真正的参数name
                                	Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
                                	if (resolvedName == null) {
                                		throw new IllegalArgumentException(
                                				"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
                                	}
                                    // 3. 根据参数name从请求中获取到参数值
                                	Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
                                	if (arg == null) {
                                		if (namedValueInfo.defaultValue != null) {
                                			arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
                                		}
                                		else if (namedValueInfo.required && !nestedParameter.isOptional()) {
                                			handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
                                		}
                                		arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
                                	}
                                	else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
                                		arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
                                	}
                                    // 4. 如果Controller上有配置@InitBinder,且作用于该request,则通过WebDataBinder里的TypeConverter看是否需要对此类型参数进行转化;
                                	if (binderFactory != null) {
                                		WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
                                		try {
                                			arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
                                		}
                                		catch (ConversionNotSupportedException ex) {
                                			throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
                                					namedValueInfo.name, parameter, ex.getCause());
                                		}
                                		catch (TypeMismatchException ex) {
                                			throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
                                					namedValueInfo.name, parameter, ex.getCause());
                                		}
                                	}
                                    // 5. 调用方法handleResolvedValue()
                                	handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
                                    // 6. 返回参数值
                                	return arg;
                                }
                                

                                AbstractNamedValueMethodArgumentResolver#resolveArgument()方法主要做五件事:

                                1. 根据方法入参获取参数的配置信息(针对@RequestParam为:拿到参数的@RequestParam注解的全部属性)。
                                  • 创建NameValueInfo的具体逻辑 由子类实现的createNamedValueInfo()方法决定。
                                  • 根据上面参数配置的name 获取真正的参数name(因为在第一步中返回的name可能是占位符或表达式,这种就需要特别处理;针对@RequestParam,这里就是@RequestParam的name()值)
                                  • 根据参数name从请求中获取到参数值;
                                    • 具体实现由子类实现的resolveName()方法决定。
                                    • 如果Controller上有配置@InitBinder,且作用于该request,则通过WebDataBinder里的TypeConverter看是否需要对此类型参数进行转化;
                                    • 调用方法handleResolvedValue();
                                      • 抽象类中为空方法,具体实现交给子类处理。
                                      • 返回参数值。

                                RequestParamMethodArgumentResolver针对第一步(getNamedValueInfo()) 和 第三步 (resolveName())做了重写;

                                createNamedValueInfo()

                                进入到createNamedValueInfo()的链路如下:

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                RequestParamMethodArgumentResolver#createNamedValueInfo()方法只是简单的提取@RequestParam注解的属性;

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                resolveName()

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                方法分为两部分:

                                1. 第一部分,判断请求是否为文件请求,是则通过request.getFiles()方法获取参数名对应的MultipartFile作为参数值。
                                2. 第二部分,非文件请求则通过request.getParameterValues方法获取参数值。
                                  • 因为请求可能是http:xxx/8080?name=1&name=2,所以返回的是数组。

                                2)处理@PathVariable标注的参数

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                @PathVariable注解用于将URL中的占位符转换为入参,其中name()和value()属性互为别名;

                                分析以如下请求为例:

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                这里特意将方法的参数名和@PathVariable中的value设置为不一样的值。

                                PathVariableMethodArgumentResolver参数解析器用于对@PathVariable注解标注的参数进行解析。

                                PathVariableMethodArgumentResolver间接实现了HandlerMethodArgumentResolver接口,直接继承了AbstractNamedValueMethodArgumentResolver抽象类。

                                1> supportsParameter()方法实现

                                判断PathVariableMethodArgumentResolver是否可以解析某个参数的逻辑如下:

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                仅有被@PathVariable注解标注的参数(参数如果是Map类型,@PathVariable注解里的name/value必须有值,否则不知道应该拿哪个KV键值对)。

                                这个逻辑和@RequestParam的一致。

                                2> createNamedValueInfo()

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                仅仅是提取@PathVariable注解的属性;

                                3> resolveName()

                                【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                根据参数名获取参数值时,这里只是简单的从请求的属性(org.springframework.web.servlet.HandlerMapping.uriTemplateVariables)中获取。

                                • 从request获取一个map类型的属性,再根据name从map中获取对应的value;

                                  org.springframework.web.servlet.HandlerMapping.uriTemplateVariables这个属性的值是从哪来的?

                                  • RequestMappingInfoHandlerMapping#lookupHandlerMethod() 方法中 中获取路径对应的HandlerMethod后,handleMatch()方法中进行的赋值;
                                  • 代码片段如下:

                                    【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                    从这里可以看出方法的入参名是什么无所谓,只需要注解中的name()和请求中的参数能对上就OK。

                                    3)处理@RequestBody标注的参数

                                    【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                    @RequestBody注解常用于POST请求,将http请求体转换成Java对象类型的参数;

                                    • **使用方式:**前端的请求body中传json格式的参数;请求头中加上Content-Type=application/json,后端用一个JAVA对象接收。
                                    • Content-Type在http协议中用于表明请求的数据内容的编码类型,服务端根据Content-Type进行相应的解码。

                                      分析以如下请求为例:

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      RequestResponseBodyMethodProcessor负责解析被@RequestBody标注的参数。

                                      RequestResponseBodyMethodProcessor间接实现了HandlerMethodArgumentResolver接口;下面看其supportsParameter()方法如何判断当前参数解析器支持解析的参数数据。

                                      1> supportsParameter()

                                      RequestResponseBodyMethodProcessor#supportsParameter()方法的逻辑表示:当前参数解析器只处理有@RequestBody注解的参数。

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      2> 参数解析

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      RequestResponseBodyMethodProcessor继承了抽象类AbstractMessageConverterMethodProcessor,并且没有重写readWithMessageConverters()方法,因此通过MessageConvert读取数据到Java类型对象中的逻辑在AbstractMessageConverterMethodProcessor类中。

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      AbstractMessageConverterMethodProcessor#readWithMessageConverters()方法主要做三件事:

                                      1. 获取请求头中的ContentType;
                                      2. 遍历所有的HttpMessageConverter 并根据ContentType找到相应的HttpMessageConverter;
                                        • 具体判断逻辑体现在MessageConverter#canRead()方法中。
                                        • 使用找到的HttpMessageConverter,将请求中的参数数据读取到相应的JavaType中。
                                          • 在将参数数据读取到JavaType前后,会遍历匹配该参数的RequestBodyAdvice,执行增强逻辑。Spring中有大量的这种扩展机制。
                                      匹配参数对应的HttpMessageConverter

                                      针对ContentType = “application/json”的请求参数,AbstractJackson2HttpMessageConverter可以对参数进行处理;

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      HttpMessageConverter将请求数据读取到JavaType

                                      读取方法readJavaType() 内部就是调用jackson包的相关类将InputStream转换为Java类型。

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      最后解析出的参数值如下:

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      HttpMessageConverter的来源?

                                      在实例化RequestMappingHandlerAdapter时,会将所有的MessageConverter加载到RequestResponseBodyMethodProcessor中:

                                      resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
                                      

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      默认情况下有十个HttpMessageConverter:

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      4、非基础类型参数

                                      1)处理Request类型的参数(比如:HttpServletRequest)

                                      分析以如下请求为例:

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      针对Request类型的参数,处理起来很简单,都是直接从请求中获取相应对象信息。

                                      就HttpServletRequest而言,ServletRequestMethodArgumentResolver参数解析器负责处理;

                                      1> 支持处理的参数类型

                                      ServletRequestMethodArgumentResolver支持处理的参数如下:

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      都是Request相关的类型 或 Request里的属性类型,比如:请求body(InputStream)、请求方式(HttpMethod)、国际化信息(Locale / TimeZone)

                                      2> 参数解析

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      解析参数的逻辑就是对类型判断,根据不同的参数类型从webRequest取对象。

                                      【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                      2)Map类型

                                      Map入参由MapMethodProcessor进行解析

                                      public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
                                          @Override
                                          public boolean supportsParameter(MethodParameter parameter) {
                                              return (Map.class.isAssignableFrom(parameter.getParameterType()) &&
                                                      parameter.getParameterAnnotations().length == 0);
                                          }
                                          @Override
                                          @Nullable
                                          public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                                                                        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
                                              Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
                                              return mavContainer.getModel();
                                          }
                                          ....
                                      }
                                      

                                      3)Model类型

                                      Model入参由ModelMethodProcessor进行解析

                                      public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
                                          @Override
                                          public boolean supportsParameter(MethodParameter parameter) {
                                              return Model.class.isAssignableFrom(parameter.getParameterType());
                                          }
                                          @Override
                                          @Nullable
                                          public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                                                                        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
                                              Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
                                              return mavContainer.getModel();
                                          }
                                          ....
                                      }
                                      

                                      无论是Model类型还是Map类型的参数,其相应参数解析器中的resolveArgument()方法并没有具体的参数处理逻辑。

                                      • 这俩基本没使用场景,一般我们传递JSON数据大多是通过@RequestBody的方式

                                        5、基础类型参数

                                        分析以如下请求为例:

                                        【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                        RequestParamMethodArgumentResolver参数解析器不仅用于解析@RequestParam、还用于特定的兜底参数解析场景;

                                        • 比如:文件类型参数(如MultipartFile),和一些简单类型(如基本类型、String、Date等)

                                          【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                          PS:解析@RequestParam 和 兜底参数解析用到的RequestParamMethodArgumentResolver是两个不同的实例对象

                                          • 解析@RequestParam时,参数解析器的useDefaultResolution=false;
                                          • 兜底解析时,参数解析器的useDefaultResolution=true

                                            【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                            因为此处RequestParamMethodArgumentResolver的父类AbstractNamedValueMethodArgumentResolver调用getNamedValueInfo方法获取参数信息时为空;

                                            【源码篇】Spring MVC多种请求入参处理方式都在这了(@RequestParam、@PathVariable、@RequestBody、Map、JavaModel、Request、基础类型)

                                            进入updateNamedValueInfo()方法时会使用反射获取到参数名。

                                            三、总结

                                            参数解析器的加载时机:

                                            • RequestMappingHandlerAdapter实现了InitializingBean接口,实例化其之后会调用afterPropertiesSet()方法,其中会对参数解析器进行赋值。
                                              • RequestMappingHandlerAdapter的参数解析器成员连变量argumentResolvers类型为HandlerMethodArgumentResolverComposite;
                                              • HandlerMethodArgumentResolverComposite是一个门面,其中组合了所有的参数解析器HandlerMethodArgumentResolver
                                              • HandlerMethodArgumentResolver是直接通过new创建的。

                                                参数解析器分为四大类:注解类型、Request请求类型、自定义、兜底实现;

                                                具体选择哪种参数解析器,由参数解析器HandlerMethodArgumentResolver#supportsParameter()方法决定;

                                                此外,Spring MVC设计了一个抽象父类AbstractNamedValueMethodArgumentResolver,用于给参数解析器提供一种通用的模板实现,子类通过重写模板中的某个方法实现自己的逻辑。


免责声明
本网站所收集的部分公开资料来源于AI生成和互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
评论列表 (暂无评论,1073人围观)

还没有评论,来说两句吧...

目录[+]