SpringMVC中HandlerMethod的请求参数解析过程
2015-11-28 14:01
507 查看
SpringMVC中HandlerMethod的请求参数解析过程
SpringMVC中处理请求的方法叫做HandlerMethod,HandlerMethod可以通过多种方式声明它的参数来源,同时对应着不同的解析过程。那么,今天我们就来详细的研究一下SpringMVC请求参数的解析过程。直接定位到参数解析的方法,下面列出了HandlerMethodInvoker的resolveHandlerArguments方法。这个方法封装了HandlerMethod参数的解析全过程,所以它很长,但是非常清晰,下面我们逐步解析。
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler, NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception { Class[] paramTypes = handlerMethod.getParameterTypes(); Object[] args = new Object[paramTypes.length]; for (int i = 0; i < args.length; i++) { MethodParameter methodParam = new MethodParameter(handlerMethod, i); methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer); GenericTypeResolver.resolveParameterType(methodParam, handler.getClass()); String paramName = null; String headerName = null; boolean requestBodyFound = false; String cookieName = null; String pathVarName = null; String attrName = null; boolean required = false; String defaultValue = null; boolean validate = false; Object[] validationHints = null; int annotationsFound = 0; Annotation[] paramAnns = methodParam.getParameterAnnotations(); for (Annotation paramAnn : paramAnns) { if (RequestParam.class.isInstance(paramAnn)) { RequestParam requestParam = (RequestParam) paramAnn; paramName = requestParam.value(); required = requestParam.required(); defaultValue = parseDefaultValueAttribute(requestParam.defaultValue()); annotationsFound++; } else if (RequestHeader.class.isInstance(paramAnn)) { RequestHeader requestHeader = (RequestHeader) paramAnn; headerName = requestHeader.value(); required = requestHeader.required(); defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue()); annotationsFound++; } else if (RequestBody.class.isInstance(paramAnn)) { requestBodyFound = true; annotationsFound++; } else if (CookieValue.class.isInstance(paramAnn)) { CookieValue cookieValue = (CookieValue) paramAnn; cookieName = cookieValue.value(); required = cookieValue.required(); defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue()); annotationsFound++; } else if (PathVariable.class.isInstance(paramAnn)) { PathVariable pathVar = (PathVariable) paramAnn; pathVarName = pathVar.value(); annotationsFound++; } else if (ModelAttribute.class.isInstance(paramAnn)) { ModelAttribute attr = (ModelAttribute) paramAnn; attrName = attr.value(); annotationsFound++; } else if (Value.class.isInstance(paramAnn)) { defaultValue = ((Value) paramAnn).value(); } else if (paramAnn.annotationType().getSimpleName().startsWith("Valid")) { validate = true; Object value = AnnotationUtils.getValue(paramAnn); validationHints = (value instanceof Object[] ? (Object[]) value : new Object[] {value}); } } if (annotationsFound > 1) { throw new IllegalStateException("Handler parameter annotations are exclusive choices - " + "do not specify more than one such annotation on the same parameter: " + handlerMethod); } if (annotationsFound == 0) { Object argValue = resolveCommonArgument(methodParam, webRequest); if (argValue != WebArgumentResolver.UNRESOLVED) { args[i] = argValue; } else if (defaultValue != null) { args[i] = resolveDefaultValue(defaultValue); } else { Class<?> paramType = methodParam.getParameterType(); if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) { if (!paramType.isAssignableFrom(implicitModel.getClass())) { throw new IllegalStateException("Argument [" + paramType.getSimpleName() + "] is of type " + "Model or Map but is not assignable from the actual model. You may need to switch " + "newer MVC infrastructure classes to use this argument."); } args[i] = implicitModel; } else if (SessionStatus.class.isAssignableFrom(paramType)) { args[i] = this.sessionStatus; } else if (HttpEntity.class.isAssignableFrom(paramType)) { args[i] = resolveHttpEntityRequest(methodParam, webRequest); } else if (Errors.class.isAssignableFrom(paramType)) { throw new IllegalStateException("Errors/BindingResult argument declared " + "without preceding model attribute. Check your handler method signature!"); } else if (BeanUtils.isSimpleProperty(paramType)) { paramName = ""; } else { attrName = ""; } } } if (paramName != null) { args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler); } else if (headerName != null) { args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler); } else if (requestBodyFound) { args[i] = resolveRequestBody(methodParam, webRequest, handler); } else if (cookieName != null) { args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler); } else if (pathVarName != null) { args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler); } else if (attrName != null) { WebDataBinder binder = resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler); boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1])); if (binder.getTarget() != null) { doBind(binder, webRequest, validate, validationHints, !assignBindingResult); } args[i] = binder.getTarget(); if (assignBindingResult) { args[i + 1] = binder.getBindingResult(); i++; } implicitModel.putAll(binder.getBindingResult().getModel()); } } return args; }这个方法的调用关系如下图所示,
看了上面的图片,可以更清晰的了解到这个方法是在请求分发时,找到了我们最常用的AnnotationMethodHandlerAdapter,最终请求到了HandlerMethodInvoker的invokeHandlerMethod方法,而在调用HandlerMethod之前需要解析出方法需要的所有参数。
一,有标注的参数的解析
Spring解析参数的第一步就是尝试根据参数使用的标注解析出参数的值。下面分别说明每种标注的参数解析过程
1,RequestParam
RequestParam是最常见的参数来源标注,它的意思是这个参数为浏览器发送过来的请求参数。对于RequestParam的解析最普遍的过程就是根据参数的名字,在Request中查找相应的Parameter。它的结果有可能是一个字符串或者是字符串数组。如果没有查到则尝试使用默认值,同时若即没有默认值还指定了required属性为true则会抛出异常。有两个特殊情况要优于从Request中直接查找,分别是Map类型的参数和MultiPart类型的Request。如果参数的类型是Map类型并且没有指定参数的名字,则认为这个Map需要所有来自浏览器的请求参数,它的解析过程就是将Request中的所有Parameter放到这个Map中。如果当前的请求是一个多部分请求,并且尝试去寻找同名的文件。
2,RequestHeader和CookieValue
RequestHeader、CookieValue形式的参数解析与RequestParam非常类似。它们都可以指定默认值,也可以指定是否必须。默认值和是否必须使用起来也是相同的方式,没有按header或cookieName找到对应的值,则尝试使用默认值。最终若还是没有解析出对应值,并且通过required指定了此参数必须则抛出异常,否则返回null(默认情况下)。
3,PathVariable
PathVariable标识的参数说明这个参数来自于RequestMapping中声明的URI中的模板变量。RequestMapping注解在 URI 模板变量中支持正则表达式,
语法 :{变量名:正则表达式},如@RequestMapping("index/{id:\\d+}")。
4,RequestBody
顾名思义RequestBody注解说明参数来自于请求体中,具体的转换过程则是在HandlerAdapter
配置的HttpMessageConverters中。一般用到这个这个注解,都需要跟客户端协商好一组协议以约定传输的内容。
5,ModelAttribute
这个注解用起来稍微有些麻烦,需要提前设置变量到ModelAttribute中,然后这里直接根据名字获取之前设置的变量。具体的解析规则会涉及到很多其他部分的知识,希望以后可以专门的研究一下吧。
上面的这些标注只能出现其中一个,如果使用了一个以上的标注则会抛出异常。如果HandlerMethod的参数通过注解的方式以及解析出了相应的值,则会先保存起来待调用方法是使用,否则还将进行进一步的解析过程。剩下的内容还挺多,留到下一篇中吧。
相关文章推荐
- 【Java小项目】电子音效器
- java实现网上下载文件到本地
- Struts2类型转换器
- Struts2类型转换器
- Java基础知识总结(我常犯的错误)
- JAVA多线程--信号量(Semaphore)
- JAVA中socket编程println()和 write()方法
- Java使用Jacob调用SAPI合成语音
- Java中java.text.SimpleDateFormat的日期格式用法
- 2.2 Spring的结构组成--Spring源码深度解析
- Java注解详解
- java编写简单五子棋游戏感悟
- Java的时间处理
- Java常见异常
- eclipse汉化过程
- 安装TC时总结的jdk心得
- subclipse 对应 Subversion 版本
- JAVA中的包(Package)
- 在eclipse中,如何添加现有java类文件或包文件到工程中?
- java泛型总结2-1之核心概念