Spring Framework源码(十二):SpringMVC之数据绑定、验证、转换

   Springmvc中的数据绑定工作得从DiapatcherServlet的doDispatch方法说起:

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    在前面的章节介绍过,这里的ha就是HandlerAdapter,我们以AnnotationMethodHandlerAdapter为例看它的handle方法究竟进行了哪些操作。

	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		Class<?> clazz = ClassUtils.getUserClass(handler);
		Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
		if (annotatedWithSessionAttributes == null) {
			annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
			this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
		}
		if (annotatedWithSessionAttributes) {
			checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
		}
		else {
			checkAndPrepare(request, response, true);
		}
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					return invokeHandlerMethod(request, response, handler);
				}
			}
		}

		return invokeHandlerMethod(request, response, handler);
	}
    我们可以看到这个方法做了很多前期准备,最关键的一步是invokeHandlerMethod,下面我们来看看这个方法。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,   HttpServletResponse response, Object handler)
			throws Exception {
                //获取方法处理器
		ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
		//resolveHandlerMethod这个方法很复杂这里略过不做详细介绍,这个方法最后帮handler找到了url对应的方法
		Method handlerMethod = methodResolver.resolveHandlerMethod(request);
		//实例化方法调用者
		ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
		//实例化ServletWebRequest
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		//实例化数据模型Model
		ExtendedModelMap implicitModel = new BindingAwareModelMap();
                //调用controller下的方法并返回结果
		Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
		//从返回结果(这个结果有可能是ModelAndView)和传入的ModelMap中获取ModelAndView
		ModelAndView mav =
				methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
		methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
		return mav;
	}

    这个方法的methodInvoker.invokeHandlerMethod,我们再来看看这个方法:

	public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
			NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
                //获取桥接方法,实际上是对handlerMethod封装了一下
		Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
		        //省略代码若干...
			boolean debug = logger.isDebugEnabled();
			//将methodResolver中的要存入session属性(带@SessionAttributes)塞入ModelMap中
			for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
				Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
				if (attrValue != null) {
					implicitModel.addAttribute(attrName, attrValue);
				}
			}
			//处理带@ModelAttribute注解的方法,将他们的结果放到ModelMap中
			for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
				Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
				Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
				if (debug) {
					logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
				}
				String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
				if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
					continue;
				}
				ReflectionUtils.makeAccessible(attributeMethodToInvoke);
				Object attrValue = attributeMethodToInvoke.invoke(handler, args);
				if ("".equals(attrName)) {
					Class<?> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
					attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
				}
				if (!implicitModel.containsAttribute(attrName)) {
					implicitModel.addAttribute(attrName, attrValue);
				}
			}
			Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
			if (debug) {
				logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
			}
			ReflectionUtils.makeAccessible(handlerMethodToInvoke);
			return handlerMethodToInvoke.invoke(handler, args);
		    //省略代码若干...
	}

    这个函数主要是将一些需要装入ModelMap的参数装入之。然后调用resolveHandlerArguments()来处理参数,包括参数的绑定、验证、转换。接下来我们看看这个函数:

	private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
			NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
                //获取参数数据类型
		Class<?>[] paramTypes = handlerMethod.getParameterTypes();
		//构造参数个数长度的数组
		Object[] args = new Object[paramTypes.length];
                //遍历args的每一个成员
		for (int i = 0; i < args.length; i++) {
			//实例化方法的参数,这是一个Springmvc的类,它描述的与该参数相联系的类、方法、参数名、注解等信息
			MethodParameter methodParam = new MethodParameter(handlerMethod, i);
			methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
			//设置参数的所属类和参数类型等信息
			GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
			//参数名
			String paramName = null;
			//http头的属性名
			String headerName = null;
			//http是否有body部分
			boolean requestBodyFound = false;
			//cookie中的属性名
			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) {
				//处理有@RequestParam注解的参数
				if (RequestParam.class.isInstance(paramAnn)) {
					RequestParam requestParam = (RequestParam) paramAnn;
					//@RequestParam的value属性
					paramName = requestParam.value();
					//@RequestParam的required属性
					required = requestParam.required();
					//@RequestParam的defaultValue属性
					defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
					annotationsFound++;
				}
				//处理带@RequestHeader注解的参数
				else if (RequestHeader.class.isInstance(paramAnn)) {
					RequestHeader requestHeader = (RequestHeader) paramAnn;
					headerName = requestHeader.value();
					required = requestHeader.required();
					defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
					annotationsFound++;
				}
				//处理带@RequestBody注解的参数
				else if (RequestBody.class.isInstance(paramAnn)) {
					requestBodyFound = true;
					annotationsFound++;
				}
				//处理带@CookieValue注解的参数
				else if (CookieValue.class.isInstance(paramAnn)) {
					CookieValue cookieValue = (CookieValue) paramAnn;
					cookieName = cookieValue.value();
					required = cookieValue.required();
					defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
					annotationsFound++;
				}
				//处理带@PathVariable注解的参数
				else if (PathVariable.class.isInstance(paramAnn)) {
					PathVariable pathVar = (PathVariable) paramAnn;
					pathVarName = pathVar.value();
					annotationsFound++;
				}
				//处理带@ModelAttribute注解的参数
				else if (ModelAttribute.class.isInstance(paramAnn)) {
					ModelAttribute attr = (ModelAttribute) paramAnn;
					attrName = attr.value();
					annotationsFound++;
				}
				//处理带@Value注解的参数
				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);
				//如果是Reuqest则赋给WebRequest实例
				if (argValue != WebArgumentResolver.UNRESOLVED) {
					args[i] = argValue;
				}
				//为参数赋予默认值
				else if (defaultValue != null) {
					args[i] = resolveDefaultValue(defaultValue);
				}
				else {
					Class<?> paramType = methodParam.getParameterType();
					//如果有Model或者Map类型的参数则直接把ModelMap实例赋给他们
					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;
					}
					//如果是SessionStatus类型的参数赋给this.sessionStatus
					else if (SessionStatus.class.isAssignableFrom(paramType)) {
						args[i] = this.sessionStatus;
					}
					//如果是HttpEntity类型的参数单独处理
					else if (HttpEntity.class.isAssignableFrom(paramType)) {
						args[i] = resolveHttpEntityRequest(methodParam, webRequest);
					}
					//如果是Errors类型的参数抛出异常状态异常
					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) {
				//处理普通参数,包括MultipartFile参数,@initBinder注解处理,普通值处理
				args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
			}
			//http报文头信息处理
			else if (headerName != null) {
				args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
			}
			//http报文body信息处理
			else if (requestBodyFound) {
				args[i] = resolveRequestBody(methodParam, webRequest, handler);
			}
			//http请求cookie信息处理
			else if (cookieName != null) {
				args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
			}
			//路径变量信息处理
			else if (pathVarName != null) {
				args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
			}
			//放入ModelMap的信息,一般是@ModelAttribute注解的
			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;
	}

      以上的代码完成了request参数到controller类下的函数的参数的绑定。resolveRequestParam会调用WebDataBinder的convertIfNecessary方法,这个方法会搜索所有注册的converter并找到支持当前参数转化的对应converter进行参数转换。doBind()方法会调用WebDataBinder的validator成员变量的binder.validate(validationHints)在绑定参数时对参数进行验证。

     Springmvc对支持的类型的转换是使用PropertyEditor对象来表示的,这个是可以由用户扩展的,我们来看看Springmvc默认支持哪些类型的转换。

private void createDefaultEditors() {
		this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64);

		// Simple editors, without parameterization capabilities.
		// The JDK does not contain a default editor for any of these target types.
		this.defaultEditors.put(Charset.class, new CharsetEditor());
		this.defaultEditors.put(Class.class, new ClassEditor());
		this.defaultEditors.put(Class[].class, new ClassArrayEditor());
		this.defaultEditors.put(Currency.class, new CurrencyEditor());
		this.defaultEditors.put(File.class, new FileEditor());
		this.defaultEditors.put(InputStream.class, new InputStreamEditor());
		this.defaultEditors.put(InputSource.class, new InputSourceEditor());
		this.defaultEditors.put(Locale.class, new LocaleEditor());
		this.defaultEditors.put(Pattern.class, new PatternEditor());
		this.defaultEditors.put(Properties.class, new PropertiesEditor());
		this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
		this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
		this.defaultEditors.put(URI.class, new URIEditor());
		this.defaultEditors.put(URL.class, new URLEditor());
		this.defaultEditors.put(UUID.class, new UUIDEditor());
		if (zoneIdClass != null) {
			this.defaultEditors.put(zoneIdClass, new ZoneIdEditor());
		}

		// Default instances of collection editors.
		// Can be overridden by registering custom instances of those as custom editors.
		this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
		this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
		this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
		this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
		this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));

		// Default editors for primitive arrays.
		this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
		this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());

		// The JDK does not contain a default editor for char!
		this.defaultEditors.put(char.class, new CharacterEditor(false));
		this.defaultEditors.put(Character.class, new CharacterEditor(true));

		// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
		this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
		this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));

		// The JDK does not contain default editors for number wrapper types!
		// Override JDK primitive number editors with our own CustomNumberEditor.
		this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
		this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
		this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
		this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
		this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
		this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
		this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
		this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
		this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
		this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
		this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
		this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
		this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
		this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));

		// Only register config value editors if explicitly requested.
		if (this.configValueEditorsActive) {
			StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
			this.defaultEditors.put(String[].class, sae);
			this.defaultEditors.put(short[].class, sae);
			this.defaultEditors.put(int[].class, sae);
			this.defaultEditors.put(long[].class, sae);
		}
	}
      Springmvc的验证功能并没有实现具体的验证规则,一般需要利用hibernate的validator这里不对hibernate做介绍。我们来看看Springmvc实现了哪些和验证有关的东西。

      HandlerMethodInvoker类下有个WebBindingInitializer类型的的成员变量,这个类的默认实现类是这样的:

    它有一个Validator成员和一个ConversionService成员这两个成员都依赖我们的配置。

    到这里SpringMVC的数据绑定、验证、转换就算讲完了。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值