在一个web请求中,参数无非是放在地址栏或者请求体中,个别请求可能会放在请求头中。放在地址栏中,我们可以获取参数如下:Stringjavaboy=request.getParameter("name");放在请求体中,如果是key/value的形式,我们可以这样获取参数:Stringjavaboy=request.getParameter("name");如果是JSON形式,我们可以通过以下方式获取输入流,然后解析成JSON字符串,再通过JSON工具转换成对象:BufferedReaderreader=newBufferedReader(newInputStreamReader(request.getInputStream()));Stringjson=reader.readLine();reader.close();Useruser=newObjectMapper().readValue(json,User.class);如果参数放在请求头中,我们可以通过以下方式获取:Stringjavaboy=request.getHeader("name");如果你使用的是Jsp/Servlet技术栈,那么参数的获取无非就是这些方法。如果使用SpringMVC框架,可能有些朋友会觉得参数获取方式太丰富了。@RequestParam、@RequestBody、@RequestHeader、@PathVariable等各种注解,参数可以是key/value或者JSON的形式,非常丰富!不过,再丰富,最底层的获取参数的方式也无外乎以上几种。那么有小伙伴要问了,SpringMVC是如何把请求中的参数提取出来直接给我们使用的呢?例如下面的接口:@RestControllerpublicclassHelloController{@GetMapping("/hello")publicStringhello(Stringname){return"hello"+name;}}我们都知道name参数是从HttpServletRequest中提取出来的,那它是怎么提取出来的呢?这就是宋弟兄今天要跟大家分享的话题。1.自定义参数解析器为了弄清楚这个问题,我们先定义一个参数解析器。自定义参数解析器需要实现HandlerMethodArgumentResolver接口。我们先来看看这个接口:publicinterfaceHandlerMethodArgumentResolver{booleansupportsParameter(MethodParameterparameter);@NullableObjectresolveArgument(MethodParameterparameter,@NullableModelAndViewContainermavContainer,NativeWebRequest@BindNullacable,Data)throwsException;}这个接口有两个方法:supportsParameter:这个方法表示是否开启这个参数解析器,返回true表示开启,和返回false表示不启用它。resolveArgument:这是具体的解析过程,即从请求中提取参数的过程,方法的返回值对应接口中参数的值。自定义参数解析器只需要实现这个接口。假设我现在有这样一个需求(其实在SpringSecurity中获取当前登录用户名是很方便的,这里只是针对这种情况,不要抬杠):假设我使用SpringSecurity做系统安全现在框架(不熟悉SpringSecurity的朋友,可以在公众号江南一电鱼后台回复ss,里面有教程),如果我在接口的参数上加上@CurrentUserName注解,那么参数的值就是当前登录的用户名,像这样:@RestControllerpublicclassHelloController{@GetMapping("/hello")publicStringhello(@CurrentUserNameStringname){return"hello"+name;}}要实现这个功能,非常容易。首先我们自定义一个@CurrentUserName注解,如下:@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.PARAMETER)public@interfaceCurrentUserName{}这个注解没什么好解释的。接下来接下来接下来我们我们自定义解析器解析器参数解析器参数参数参数参数参数参数参数参数解析器解析器定义}@OverridepublicObjectresolveArgument(MethodParameterparameter,ModelAndViewContainermavContainer,NativeWebRequestwebRequest,WebDataBinderFactorybinderFactory)抛出异常{Useruser=(User)SecurityContextHolder.getContext().getAuthentication().getPrincipal().getuser);}supportsParameter:如果参数类型为String且参数上有@CurrentUserName注解,则使用该参数解析器。resolveArgument:该方法的返回值为参数的具体值,可以从SecurityContextHolder中获取当前登录用户名。最后,我们将自定义参数解析器配置到HandlerAdapter中,如下所示:如果配置完成。接下来,启动项目。用户登录成功后,访问/hello接口,可以看到返回了当前登录的用户数据。这是我们的自定义参数类型解析器。如您所见,这非常容易。在SpringMVC中,默认有很多HandlerMethodArgumentResolver的实现类,它们处理的问题也大同小异。宋弟兄再举个例子。2.PrincipalMethodArgumentResolver如果我们在项目中使用SpringSecurity,我们可以通过以下方式获取当前登录用户信息:@GetMapping("/hello2")publicStringhello2(Principalprincipal){return"hello"+principal.getName();}即直接在当前接口的参数上增加一个Principal类型的参数。该参数描述了当前登录用户的信息。这位用过SpringSecurity的小伙伴应该都知道。那么这个功能是如何实现的呢?当然,PrincipalMethodArgumentResolver正在工作!我们来看看这个参数解析器:}@OverridepublicObjectresolveArgument(MethodParameterparameter,@NullableModelAndViewContainermavContainer,NativeWebRequestwebRequest,@NullableWebDataBinderFactorybinderFactory)throwsException{HttpServletRequestrequest=webRequest.getNativeRequest(HttpServletRequest.class);if(request==null){thrownewIllegalStateException("当前请求不是HttpServletRequest类型:"+webRequest);}Principalprincipal=request.getUserPrincipal();if(principal!=null&&!parameter.getParameterType().isInstance(principal)){thrownewIllegalStateException("Currentuserprincipalisnotoftype["+parameterr.getParameterType().getName()+"]:"+principal);}返回本金;}}supportsParameter:该方法主要是判断参数类型是否为Principal。如果参数类型是Principal,则支持resolveArgument:这个方法逻辑很简单,先获取原始请求,然后从请求中获取Principal对象并返回。是不是很简单,有了这个,我们就可以随时加载当前登录的用户信息了。3.RequestParamMapMethodArgumentResolver松哥再给大家举个例子:@RestControllerpublicclassHelloController{@PostMapping("/hello")publicvoidhello(@RequestParamMultiValueMapmap)throwsIOException{//省略...}}这个里面有很多小伙伴interface可能写的是使用Map接收前端传来的参数,那么这里使用的参数解析器就是RequestParamMapMethodArgumentResolver。公共类RequestParamMapMethodArgumentResolver实现HandlerMethodArgumentResolver{@OverridepublicbooleansupportsParameter(MethodParameterparameter){RequestParamrequestParam=parameter.getParameterAnnotation(RequestParam.class);return(requestParam!=null&&Map.class.isAssignableFrom(parameter.getParameterType())&&!StringUtils.hasText(requestParam.name()));}@OverridepublicObjectresolveArgument(MethodParameterparameter,@NullableModelAndViewContainermavContainer,NativeWebRequestwebRequest,@NullableWebDataBinderFactorybinderFactory)throwsException{ResolvableTyperesolvableType=ResolvableType.forMethodParameter(参数);if(MultiValueMap.class.isAssignableFrom(parameter.getParameterType())){//MultiValueMapClassvalueType=resolvableType.as(MultiValueMap.class).getGeneric(1).resolve();if(valueType==MultipartFile.class){MultipartRequest多部分请求=MultipartResolutionDelegate.resolveMultipartRequest(webRequest);return(multipartRequest!=null?multipartRequest.getMultiFileMap():newLinkedMultiValueMap<>(0));}elseif(valueType==Part.class){HttpServletRequestservletRequest=webRequest.getNativeRequest(HttpServletRequest.class);如果(servletRequest!=null&&MultipartResolutionDelegate.isMultipartRequest(servletRequest)){Collection
