您的位置:首页 > 编程语言 > Java开发

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的参数通过注解的方式以及解析出了相应的值,则会先保存起来待调用方法是使用,否则还将进行进一步的解析过程。剩下的内容还挺多,留到下一篇中吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: