交流
商城
MCN
登入
注册
首页
提问
分享
讨论
建议
公告
动态
发表新帖
发表新帖
springmvc第4-demo-2:@RequestParam注解的解析 RequestParamMethodArgumentResolver
分享
未结
0
970
李延
LV6
2021-05-28
悬赏:20积分
fu# 使用 我们创建RequestParam 注解的方法,如下: ```java @RestController @RequestMapping("requestParam") public class RequestParamTest { @GetMapping("get/test") private List<String> getTest(@RequestParam String name){ System.out.println(name); return new ArrayList<>(); } @PostMapping("post/test") private List<String> postTest(@RequestParam String name){ System.out.println(name); return new ArrayList<>(); } } ``` 这里我们一共2个请求,一个post,一个get。我们分别用2种不同的传参方式看一下具体的。我们分发送以下请求 ```http ### get请求 GET http://127.0.0.1:8080/requestParam/get/test?name=123 ### post请求 POST http://127.0.0.1:8080/requestParam/post/test Content-Type: application/x-www-form-urlencoded name=liy ### post请求.多个值传过来 POST http://127.0.0.1:8080/requestParam/post/test?name=1244 Content-Type: application/x-www-form-urlencoded name=liy&name=liydw ``` 我们通过上面的示例知道,RequestParam注解支持以下参数解析: 1. 我们通过url?后的传参方法。 2. 对于application/x-www-form-urlencoded参数类型解析 并且通过示例3,我们看到当传入多个同名参数是,spring会将其以 ,拼接在一起。  # 源码解析 ## 继承关系  ## HandlerMethodArgumentResolver 顶级接口,定义supportsParameter 和 resolveArgumentjie 接口,判断是否支持当前参数解析与进行解析。 ## AbstractNamedValueMethodArgumentResolver 抽象的方法名称参数解析器,很多参数解析都是继承它。我们看它的resolveArgument方法 ```java @Override @Nullable public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { //将参数信息分装为NamedValueInfo对象,包括:参数名称、是否为必须、默认值 NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); MethodParameter nestedParameter = parameter.nestedIfOptional(); //解决表达式 Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name); if (resolvedName == null) { throw new IllegalArgumentException( "Specified name must not resolve to null: [" + namedValueInfo.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); } // 格式转换 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()); } // Check for null value after conversion of incoming argument value if (arg == null && namedValueInfo.defaultValue == null && namedValueInfo.required && !nestedParameter.isOptional()) { handleMissingValue(namedValueInfo.name, nestedParameter, webRequest); } } handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); return arg; } ``` 我们看到首先将参数信息统一分装为NamedValueInfo对象,这一步具体由子实现。 之后参数名称中可能有表达式,所以通过beanFacory解析一遍,这一步就是用的BeanFactory方法。我们在spring核心章节有说明。 再然后是获取具体的参数值,这一步根据不同参数类型,由子类自己实现。 之后又是统一的默认值处理。 解析来是对于数据格式的转换,而这一步是通过binderFactory来完成,我们在WebDataBinder章节说明。 ## RequestParamMethodArgumentResolver 通过父类我们看到父类是一个模版方法,其将分装NamedValueInfo和参数值的获取留给了子类实现。所以我们下面重点关注 ### supportsParameter 判断当前解析器是否支持当前参数。 ```java @Override public boolean supportsParameter(MethodParameter parameter) { if (parameter.hasParameterAnnotation(RequestParam.class)) { if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); return (requestParam != null && StringUtils.hasText(requestParam.name())); } else { return true; } } else { if (parameter.hasParameterAnnotation(RequestPart.class)) { return false; } parameter = parameter.nestedIfOptional(); if (MultipartResolutionDelegate.isMultipartArgument(parameter)) { return true; } else if (this.useDefaultResolution) { return BeanUtils.isSimpleProperty(parameter.getNestedParameterType()); } else { return false; } } } ``` 通过上面代码我们看到支持以下内容: 1、包含RequestParam注解的参数。 2、包含注解并且数据类型为Map。同时要求name不为空。 3、不包含RequestPart注解的文件上传。 4、基本数据类型 ### useDefaultResolution ```java @Override protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { RequestParam ann = parameter.getParameterAnnotation(RequestParam.class); return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo()); } ``` 对于分装NamedValueInfo对象,就是去解析RequestParam注解过程。 ### resolveName 这个是我们关注重点,参数的获取过程就在这里 ```java @Override @Nullable protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class); if (servletRequest != null) { Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest); if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) { return mpArg; } } 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; } ``` 前面两个方法是对文件的处理。我们先不关注。 在最后面的方法中。我们看到数据的获取是通过tomcat 提供的getParameterValues方法直接获取
回帖
消灭零回复
提交回复
热议榜
java 相关知识分享
8
好的程序员与不好的程序员
6
写给工程师的十条精进原则
5
spring boot以jar包运行配置的logback日志文件没生成
5
一步一步分析SpringBoot启动源码(一)
5
MockMvc测试
5
【吐槽向】是不是有个吐槽的板块比较好玩
4
logstash jdbc同步mysql多表数据到elasticsearch
3
IntelliJ IDEA 优质License Server
3
.gitignore忽略规则
3
SpringBoot启动源码分析
3
一步一步分析SpringBoot启动源码(三)
3
2
一步一步分析SpringBoot启动源码(二)
2
积分不够将无法发表新帖
2
官方产品
Meta-Boot - 基于MCN
MCN - 快速构建SpringBoot应用
微信扫码关注公众号