SpringMVC - 运行流程详解-表单提交到视图返回

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/J080624/article/details/58041191

上面讲述的是从 JSP的 a 标签发出请求到获取视图页面。该篇简要描述一下从填写表单页面到后台成功响应的过程。

常见参数解析器:

这里写图片描述

JSP页面

这里使用了form标签:

<form:form action="${pageContext.request.contextPath }/emp" method="POST" 
modelAttribute="employee">

		<c:if test="${employee.id == null }">
			<!--【 path 属性对应 html 表单标签的 name 属性值】 -->
			LastName: <form:input path="lastName"/>
		</c:if>
		
		<!--   当id不为空的时候,才将请求由post 转为put请求,适应修改操作 -->
		<c:if test="${employee.id != null }">
			<form:hidden path="id"/>
			<input type="hidden" name="_method" value="PUT"/>
		</c:if>
		
		<br><br>
		Email: <form:input path="email"/>
		<br><br>
		<% 
			Map<String, String> genders = new HashMap();
			genders.put("1", "Male");
			genders.put("0", "Female");
			
			request.setAttribute("genders", genders);
		%>
		Gender: 
		<br><br>
		<form:radiobuttons path="gender" items="${genders}" delimiter=" " />
		<br><br>
		
		<!--itemValue 为集合中bean的属性,集合为bean的集合	-->
		Department: 
		<form:select path="department.id" items="${departments }" itemLabel="departmentName" itemValue="id">
			<form:option value="0">请选择</form:option>
		</form:select>
		<br><br>
		
		Birth: <form:input path="birth"/>
		<br><br>
		Salary: <form:input path="salary"/>
		<br><br>
		<input type="submit" value="Submit"/>
	</form:form>

后台code

  • code1( @ModelAttribute注解的方法,在每个方法响应前先执行该房屋):
@ModelAttribute
public void getEmployee(@RequestParam(value="id1",required=false) Integer id,Map<String, Object> map){
	if(id != null){
		map.put("employee", employeeDao.get(id));
	}
	System.out.println("@ModelAttribute  方法执行");
}
  • code2:
	@RequestMapping(value="/emp", method=RequestMethod.POST)
	public String save(@Valid Employee employee, Errors result, 
			Map<String, Object> map){
		System.out.println("save: " + employee);
		
		if(result.getErrorCount() > 0){
			System.out.println("出错了!");
			//校验错误信息
			for(FieldError error:result.getFieldErrors()){
				System.out.println(error.getField() + ":" + error.getDefaultMessage());
			}
			
			//若验证出错, 则转向定制的页面
			map.put("departments", departmentDao.getDepartments());
			return "input";
		}
		
		employeeDao.save(employee);
		return "forward:/emps";
		//非具体视图,而是再次转发到String list()最后返回list
	}
  • code3:
	@RequestMapping("/emps")
	public String list(Map<String, Object> map){
		map.put("employees", employeeDao.getAll());
		System.out.println("list method execute...");
		return "list";
	}

下面开始根据源码追溯整个流程。

【1】DispatcherServlet.doDispatcher()

(1)根据handlerMapping获取HandlerExecutionChain:

/**
	 * Return the HandlerExecutionChain for this request.
	 * <p>Tries all handler mappings in order.
	 * @param request current HTTP request
	 * @return the HandlerExecutionChain, or {@code null} if no handler could be found
	 */
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			/*根据handlerMapping拿到handlerExecutionChain*/
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

(2)通过HandlerExecutionChain获取handler从而得到handlerAdapter

HandlerExecutionChain mappedHandler  = 
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  • 【getHandlerAdapter()】
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		for (HandlerAdapter ha : this.handlerAdapters) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			if (ha.supports(handler)) {
				return ha;
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

  • POST 方法,跳过if判断,直接执行拦截器前置方法;
String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						String requestUri = urlPathHelper.getRequestUri(request);
						logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

【2】RequestMappingHandlerAdapter.handle!!!

go on and go on…

  • HandlerMethod handlerMethod通过HandlerExecutionChain获得;

来到办实事的方法ModelAndView invokeHandleMethod():

private ModelAndView invokeHandleMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);

		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
		ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);

		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();

			if (logger.isDebugEnabled()) {
				logger.debug("Found concurrent result value [" + result + "]");
			}
			requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
		}

		requestMappingMethod.invokeAndHandle(webRequest, mavContainer);

		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}

		return getModelAndView(mavContainer, modelFactory, webRequest);
	}

  • 【ModelAndView handleInternal】:

protected final ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			// Always prevent caching in case of session attribute management.
			checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
		}
		else {
			// Uses configured default cacheSeconds setting.
			checkAndPrepare(request, response, true);
		}

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					return invokeHandleMethod(request, response, handlerMethod);
				}
			}
		}

		return invokeHandleMethod(request, response, handlerMethod);

【第一步:创建数据绑定工厂:】

  • 此时绑定的方法为空;
  • binderFactory类型为ServletRequestDataBinderFactory;

这里写图片描述

initializer基本信息如下:

这里写图片描述


【第二步:创建modelFactory】

  • attributeMethods 只有一个方法–getEmployee;
  • 数据绑定工厂–上面刚建立的;
  • sessionAttributeHandler!!!判断你的controller有没有使用@SessionAttributes注解;

这里写图片描述


【第三步:创建请求映射方法对象】

  • 参数为物理映射的方法(save)和数据绑定工厂;

这里写图片描述.

  • 方法如下:
private ServletInvocableHandlerMethod createRequestMappingMethod(
			HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {

		ServletInvocableHandlerMethod requestMethod;
		requestMethod = new ServletInvocableHandlerMethod(handlerMethod);
		//包装成ServletInvocableHandlerMethod
		requestMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		//设置参数解析器
		requestMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		//设置方法返回值处理器
		requestMethod.setDataBinderFactory(binderFactory);
		//为方法对象绑定 binderFactory
		requestMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
		//设置参数发现者
		
		return requestMethod;
	}

如此之后,下面才可以使用该方法对象进行解析。


【第四步:创建MV容器,并尝试将request中的modelmap放入mv容器】

ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
  • defaultModel:BindingAwareModelMap
  • redirectModel:null
  • requestHandled:false(注意该属性,在最后一步获得mv时将会用到)
  • sessionStatus:SimpleSessionStatus(有属性complete,同样在获得mv时会用到)

这里写图片描述



【第五步:initModel!】

Map<String, ?> attributesInSession = this.sessionAttributesHandler.retrieveAttributes(request);
mavContainer.mergeAttributes(attributesInSession);
//看session中有没有model,有了copy到mav容器

invokeModelAttributeMethods(request, mavContainer);
//调用@ModelAttribute注解的方法

注释:先解析方法的参数值,然后调用该方法,最后拿到返回值。

Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
/*注意:在这个方法里,解析了@ModelAttribute注解的方法,为参数赋值,并进行了数据格式化!!!*/

  • 【解析目标方法参数】:

  • 【方法getMethodArgumentValues】:
/**
	 * Get the method argument values for the current request.
	 */
	private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		
	/*Returns the method parameters for this handler method. 返回parameters对象数组,每一个对象包含了对应的方法参数类型和其他信息 刚开始对象里面方法参数类型为null*/
		MethodParameter[] parameters = getMethodParameters();
		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			
			/*Initialize parameter name discovery for this method parameter. */
	parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
	
			/*Determine the target type for the given generic parameter type.---确定给定泛型参数类型的目标类型。*/
	GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
	
	/*Attempt to resolve a method parameter from the list of provided argument values.*/
			args[i] = resolveProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			if (this.argumentResolvers.supportsParameter(parameter)) {
				try {
					args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
					continue;
				}
				catch (Exception ex) {
					if (logger.isTraceEnabled()) {
						logger.trace(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
					}
					throw ex;
				}
			}
			if (args[i] == null) {
				String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
				throw new IllegalStateException(msg);
			}
		}
		return args;
	}

  • 【方法getMethodParameters()】:
/**
	 * Returns the method parameters for this handler method.
	 */
	public MethodParameter[] getMethodParameters() {
		return this.parameters;
	}

此时的参数解析器:

  • RequestParamMethodArgumentResolver 【简单参数类型默认使用该解析器】;

这里写图片描述


方法如下:

	@Override
	public final Object resolveArgument(
			MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
			throws Exception {

		Class<?> paramType = parameter.getParameterType();
		//获取namedValueInfo,属性name对应方法参数的key
		NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
		
		//这里使用RequestParamMethodArgumentResolver进行解析;
		Object arg = resolveName(namedValueInfo.name, parameter, webRequest);
		if (arg == null) {
			if (namedValueInfo.defaultValue != null) {
				arg = resolveDefaultValue(namedValueInfo.defaultValue);
			}
			else if (namedValueInfo.required) {
				handleMissingValue(namedValueInfo.name, parameter);
			}
			arg = handleNullValue(namedValueInfo.name, arg, paramType);
		}
		else if ("".equals(arg) && (namedValueInfo.defaultValue != null)) {
			arg = resolveDefaultValue(namedValueInfo.defaultValue);
		}

		if (binderFactory != null) {
		
			//我要创建并初始化binder对象,尝试放conversionService和validator等进去---注意此时binder的bindingresult为null!!!
			WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
			
			//如果必要还会进行数据格式化-Convert the value to the required type (if necessary from a String). 
			arg = binder.convertIfNecessary(arg, paramType, parameter);
		}

		//看这里,处理解析得到的value--有两个方法
		handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

		return arg;
	}

  • 【方法getNamedValueInfo】:
/**
	 * Obtain the named value for the given method parameter.
	 */
	private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
		
		//首先尝试获取
		NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
		//如果为空则创建,如方法参数没有显示声明key
		if (namedValueInfo == null) {
			namedValueInfo = createNamedValueInfo(parameter);
			namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
			this.namedValueInfoCache.put(parameter, namedValueInfo);
		}
		return namedValueInfo;
	}

**这里需要注意的事,namedValueInfo的name属性即为参数对应的key:

① 如果用@RequestParam(key)显示声明,那么参数对应的key为括号内显示声明的;

② 如果没有显示声明,那么为参数本身;

如下图所示(以前均为未使用RequestParam显示声明的):

name = “Id”;

这里写图片描述

name=“ID”

这里写图片描述


RequestParamMethodArgumentResolver.resolveName

    • 普通参数解析器的解析过程
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {

		Object arg;

		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		MultipartHttpServletRequest multipartRequest =
			WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);

-- 先判断是不是文件
		if (MultipartFile.class.equals(parameter.getParameterType())) {
			assertIsMultipartRequest(servletRequest);
			Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
			arg = multipartRequest.getFile(name);
		}
		else if (isMultipartFileCollection(parameter)) {
			assertIsMultipartRequest(servletRequest);
			Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
			arg = multipartRequest.getFiles(name);
		}
		else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) {
			assertIsMultipartRequest(servletRequest);
			arg = servletRequest.getPart(name);
		}
		else if (isPartCollection(parameter)) {
			assertIsMultipartRequest(servletRequest);
			arg = new ArrayList<Object>(servletRequest.getParts());
		}
		else {
			arg = null;
			if (multipartRequest != null) {
				List<MultipartFile> files = multipartRequest.getFiles(name);
				if (!files.isEmpty()) {
					arg = (files.size() == 1 ? files.get(0) : files);
				}
			}
--不是文件,那么 arg 此时为null
			if (arg == null) {
	-- 注意这里,使用了我们最熟悉的获取参数的方式!!!!
				String[] paramValues = webRequest.getParameterValues(name);
				if (paramValues != null) {
					arg = paramValues.length == 1 ? paramValues[0] : paramValues;
				}
			}
		}

		return arg;
	}


解析得到的参数值:

这里写图片描述


方法-PathVariableMethodArgumentResolver.handleResolvedValue

  • 该次没有调用该方法
protected void handleResolvedValue(Object arg, String name, MethodParameter parameter,
			ModelAndViewContainer mavContainer, NativeWebRequest request) {

		String key = View.PATH_VARIABLES;
		int scope = RequestAttributes.SCOPE_REQUEST;
		Map<String, Object> pathVars = (Map<String, Object>) request.getAttribute(key, scope);
		if (pathVars == null) {
			pathVars = new HashMap<String, Object>();
			request.setAttribute(key, pathVars, scope);
		}
		pathVars.put(name, arg);
	}


解析第二个参数,参数类型为map

  • 此时解析器为.MapMethodProcessor
public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler

方法如下:

    • map解析器就是返回mavContainer里面的model!!!
public Object resolveArgument(
			MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
			throws Exception {
		//就是一句话,返回mav里面的model
		return mavContainer.getModel();
	}

mavContainer.getModel();

/**
	 * Return the model to use: the "default" or the "redirect" model.
	 * <p>The default model is used if {@code "redirectModelScenario=false"} or
	 * if the redirect model is {@code null} (i.e. it wasn't declared as a
	 * method argument) and {@code ignoreDefaultModelOnRedirect=false}.
	 */

	public ModelMap getModel() {
		//这里的defaultModel为BindingAwareModelMap
		if (useDefaultModel()) {
			return this.defaultModel;
		}
		else {
			return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
		}
	}

@ModelAttribute注解的方法有两个参数,第一个为 Integer id,第二个为map。在解析第一个id时,使用了binder进行参数格式化转换。但是不用校验,故而BindingResult为null。解析第二个参数map类型时,并未用到binder而是直接返回了mavContainer.getModel()


此时的ModelAndViewContainer: View is [null]; default model {}

至此 @ModelAttribute注解的方法结束,mav容器有了初步填充!!!


initModel方法结束!!!initModel方法结束!!!initModel方法结束!!!


开始执行真正的目标方法!!!开始执行真正的目标方法!!!开始执行真正的目标方法!!!



【第六步:处理真正的目标方法-save】

  • 该方法有三个参数:Employee employee, Errors result, Map<String, Object> map
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
  • ServletInvocableHandlerMethod.invokeAndHandle
public final void invokeAndHandle(ServletWebRequest webRequest,
			ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
		// 在这里调用父类(.InvocableHandlerMethod)的invokeForRequest
		//这个方法意思是:解析完目标方法的参数值后调用其方法,并拿到方法的返回值
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(this.responseReason)) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);

		try {
		//在这里处理方法的返回值!!!!
			this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
			}
			throw ex;
		}
	}


【第七步:使用父类的方法解析参数】

public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
			
		//在这里拿到解析得到的参数值数组
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			StringBuilder sb = new StringBuilder("Invoking [");
			sb.append(this.getBeanType().getSimpleName()).append(".");
			sb.append(getMethod().getName()).append("] method with arguments ");
			sb.append(Arrays.asList(args));
			logger.trace(sb.toString());
		}
		/*在这里调用真正的物理目标方法 --第一次为save */
		Object returnValue = invoke(args);
		if (logger.isTraceEnabled()) {
			logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
		}
		//把方法的返回值,返回上级方法
		return returnValue;
	}

【第八步:解析参数-循环遍历解析参数】

args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);

② —拿到对应的参数解析器

@Override
	public Object resolveArgument(
			MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
			throws Exception {
		//根据parameter,拿到对应的resolver!!!
		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}

第一个参数为Employee employee,参数类型和resolver如下所示:

  • 参数解析器为:ServletModelAttributeMethodProcessor!!!

这里写图片描述

这里写图片描述


【第九步:.ModelAttributeMethodProcessor.resolveArgument】

  • 解析参数类型为Employee的参数
  • 注意该类的resolveArgument与上一个运行流程示例不同!
  • 数据的绑定,格式化以及校验都在这个方法里面
@Override
	public final Object resolveArgument(
			MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest request, WebDataBinderFactory binderFactory)
			throws Exception {
			
		//根据MethodParameter parameter对象 拿到 name(key)
		String name = ModelFactory.getNameForParameter(parameter);
		
		//判断mav里面有没有,没有就根据参数类型创建一个空的对象
		Object attribute = (mavContainer.containsAttribute(name)) ?
				mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
		
		//创建binder对象--不同解析器的解析参数方法此处会有不同!!!
		WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
		if (binder.getTarget() != null) {
		
			//如果target不为空,则进行绑定和数据格式化
			bindRequestParameters(binder, request);
			//调用注册的validators,进行校验
			validateIfApplicable(binder, parameter);
			if (binder.getBindingResult().hasErrors()) {
				if (isBindExceptionRequired(binder, parameter)) {
					throw new BindException(binder.getBindingResult());
				}
			}
		}


① 创建attribute

@Override
	protected final Object createAttribute(String attributeName,MethodParameter parameter,WebDataBinderFactory binderFactory,NativeWebRequest request) throws Exception {
		
		//根据name尝试从request中取值
		String value = getRequestValueForAttribute(attributeName, request);
		if (value != null) {
			Object attribute = createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request);
			if (attribute != null) {
				return attribute;
			}
		}
		//调用父类的方法
		return super.createAttribute(attributeName, parameter, binderFactory, request);
	}


【第十步:创建binder对象】


  • 此时仍在ServletModelAttributeMethodProcessor.resolveArgument
  • 此时name为employee;
  • attribute为空的Employee对象;
  • 根据name、attribute和request创建binder对象;

【 何为binder对象???何为binder对象???】

  • 以下为binder对象的基本属性:

一般包括objectName(key),target(value),bindingResult(校验结果),conversionService(数据转换和格式化)以及validators(校验器)等

这里写图片描述


看errors与BindingResult之间的家庭族谱:

这里写图片描述


看WebDataBinder WebDataBinder extends DataBinder注释:

Special DataBinder for data binding from web request parameters to JavaBean objects.
--将请求参数绑定到JavaBean 对象(这就是主要功能!!!)
--因为方法参数Employee employee 为Java bean ,所以需要使用binder!!!

 Designed for web environments, but not dependent on the Servlet API; 
 serves as base class for more specific DataBinder variants, 
 such as org.springframework.web.bind.ServletRequestDataBinder. 


  • 创建binder方法
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);

这里写图片描述


下面就明白了,就是把请求中的表单参数一个个绑定到java bean对应的属性上!!!


① 具体创建binder对象方法

@Override
	public final WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
			throws Exception {
		WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
		if (this.initializer != null) {
			this.initializer.initBinder(dataBinder, webRequest);
		}
		initBinder(dataBinder, webRequest);
		return dataBinder;
	}
  • 初始化binder时,设置validator和conversionService:

这里写图片描述

binder初始化完毕,属性如下:

这里写图片描述

此时属性bindingResult仍旧为null



【第十一步:绑定请求参数值】

bindRequestParameters(binder, request);

②–把请求参数值绑定到java bean object上面(Employee employee)

protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
		ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
		ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
		servletBinder.bind(servletRequest);
	}

③ --真正的bind方法

class:ServletRequestDataBinder

public void bind(ServletRequest request) {

		//注意这里!!!说明了mpvs从哪里来!!!!
		MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
		MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
		if (multipartRequest != null) {
			bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
		}
		/*Extension point that subclasses can use to add extra bind values for a request. Invoked before doBind(MutablePropertyValues). The default implementation is empty.*/
		//从请求中获取uri的 key:value,添加到mpvs里面。
		addBindValues(mpvs, request);
		//执行真正的绑定--将mpvs里面的key:value,一一绑定到Java bean
		doBind(mpvs);
	}

  • 此时的mpvs的 elementData长度为六,即employee的六个属性

这里写图片描述

  • 其中,属性对应的值如下:

    • 每一个元素是个map对象,其中包含了 property以及对应的value!!!

这里写图片描述


注释如下:Merge URI variables into the property values to use for data binding.

—就是拿URL里面的参数放到mpvs里面!

protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {
		String attr = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
		Map<String, String> uriVars = (Map<String, String>) request.getAttribute(attr);
		if (uriVars != null) {
			for (Entry<String, String> entry : uriVars.entrySet()) {
				if (mpvs.contains(entry.getKey())) {
					logger.warn("Skipping URI variable '" + entry.getKey()
							+ "' since the request contains a bind value with the same name.");
				}
				else {
					mpvs.addPropertyValue(entry.getKey(), entry.getValue());
				}
			}
		}
	}

好了,更新完mpvs了,URL里面的参数也拿到了!


⑤ 真正开始绑定

  • 为参数Employee employee赋值;
  • 此时的class:public class WebDataBinder extends DataBinder 。
@Override
	protected void doBind(MutablePropertyValues mpvs) {
	/*Check the given property values for field defaults, i.e. for fields that start with the field default prefix. 

The existence of a field defaults indicates that the specified value should be used if the field is otherwise not present.
*/
		checkFieldDefaults(mpvs);
		/*Check the given property values for field markers, i.e. for fields that start with the field marker prefix. 

The existence of a field marker indicates that the specified field existed in the form. If the property values do not contain a corresponding field value, the field will be considered as empty and will be reset appropriately.
*/
		checkFieldMarkers(mpvs);
		//检测完了,开始调用父类的boBind
		super.doBind(mpvs);
	}

⑥ 父类的doBind

此时class:DataBinder implements PropertyEditorRegistry, TypeConverter

protected void doBind(MutablePropertyValues mpvs) {
		/*Check the given property values against the allowed fields, removing values for fields that are not allowed.*/
		checkAllowedFields(mpvs);
		/*Check the given property values against the required fields, generating missing field errors where appropriate.*/
		checkRequiredFields(mpvs);
		//赋值!!!
		/*Apply given property values to the target object. 
		--为目标对象赋值(target对象)
Default implementation applies all of the supplied property values as bean property values. By default, unknown fields will be ignored.*/

		applyPropertyValues(mpvs);
	}


⑦ 重头戏开始 void applyPropertyValues

—还记得吧,此时的target(即前面根据目标方法参数类型创建的attribute)还为空的Employee对象

protected void applyPropertyValues(MutablePropertyValues mpvs) {
		try {
			// Bind request parameters onto target object.开始为target 对象 赋值啦!!!另外注意,开始用到bindingResult了!
			getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
		}
		catch (PropertyBatchUpdateException ex) {
			// Use bind error processor to create FieldErrors.
			for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
				getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
			}
		}
	}

⑧ 首先你要获取getPropertyAccessor()

方法注释:Return the underlying PropertyAccessor of this binder's BindingResult.

protected ConfigurablePropertyAccessor getPropertyAccessor() {

		//从bindingResult里面获取PropertyAccessor
		return getInternalBindingResult().getPropertyAccessor();
	}

getInternalBindingResult()

  • 初始化bindingResult!!!

方法注释:Return the internal BindingResult held by this DataBinder, as AbstractPropertyBindingResult.

protected AbstractPropertyBindingResult getInternalBindingResult() {
		if (this.bindingResult == null) {
			//如果bindingResult为空,则进行初始化!!!
			initBeanPropertyAccess();
		}
		return this.bindingResult;
	}

org.springframework.validation.BindingResult

General interface that represents binding results. 
--呈现绑定结果的通用接口。
Extends the interface for error registration capabilities, 
--扩展了该接口的错误注册能力
allowing for a Validator to be applied, and adds binding-specific analysis and model building. 
--允许校验器使用,并增加了特定绑定分析和model 构建

Serves as result holder for a DataBinder, obtained via the DataBinder.getBindingResult() method. 
--可以通过DataBinder.getBindingResult() 方法获得该对象(作为result holder)。

BindingResult implementations can also be used directly, for example to invoke a Validator on it (e.g. as part of a unit test).

/*也就是说,会把model放进去,并对target--JavaBean object 进行校验,校验结果Errors放进bindingResult!!!*/

void initBeanPropertyAccess()

方法注释如下:Initialize standard JavaBean property access for this DataBinder. This is the default; an explicit call just leads to eager initialization.
–为DataBinder初始化 属性(javaBean property)访问通道;该方式是默认的,如果想更早,也可以显示调用 。

public void initBeanPropertyAccess() {
		
		/*如果bindingResult为null,就创建*/
		Assert.state(this.bindingResult == null,
				"DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods");
				
				/*Default implementation of the Errors and BindingResult interfaces, for the registration and evaluation of binding errors on JavaBean objects. 
				/*Errors或者bindingResult接口的默认实现类,实现JavaBean objects的 注册和评估错误绑定能力*/
Performs standard JavaBean property access, also supporting nested properties.
/*Performs standard JavaBean property access, also supporting nested properties.*/ 
Normally, application code will work with the Errors interface or the BindingResult interface. 
/*通常,应用代码会使用Errors或者BindingResult接口工作*/
A DataBinder returns its BindingResult via DataBinder.getBindingResult().
/*一个数据绑定对象通过getBindingResult()方法返回它的BindingResult*/
*/

		this.bindingResult = new BeanPropertyBindingResult(
				getTarget(), getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit());
				/*放进去target 和objectName!!!*/
		if (this.conversionService != null) {
			this.bindingResult.initConversion(this.conversionService);
			/* 初始化Conversion!!!!*/
		}
	}
  • 如下图所示,target对象和objectName对象从databinder中获取【同时表明,如果在】:

这里写图片描述


  • 【BeanPropertyBindingResult 】

  • 有属性target;

  • 无属性objectName,从父类AbstractPropertyBindingResult拿;

@SuppressWarnings("serial")
public class BeanPropertyBindingResult extends AbstractPropertyBindingResult implements Serializable {
	
	//有属性target
	private final Object target;

	private final boolean autoGrowNestedPaths;

	private final int autoGrowCollectionLimit;
	
	//beanWrapper
	private transient BeanWrapper beanWrapper;


	/**
	 * Creates a new instance of the {@link BeanPropertyBindingResult} class.
	 * @param target the target bean to bind onto
	 * @param objectName the name of the target object
	 */
	public BeanPropertyBindingResult(Object target, String objectName) {
		this(target, objectName, true, Integer.MAX_VALUE);
	}

	/**
	 * Creates a new instance of the {@link BeanPropertyBindingResult} class.
	 * @param target the target bean to bind onto
	 * @param objectName the name of the target object
	 * @param autoGrowNestedPaths whether to "auto-grow" a nested path that contains a null value
	 * @param autoGrowCollectionLimit the limit for array and collection auto-growing
	 */
	public BeanPropertyBindingResult(Object target, String objectName, boolean autoGrowNestedPaths, int autoGrowCollectionLimit) {
		//调用父类的该方法。。。直到调用父类AbstractBindingResult的方法
		super(objectName);
		this.target = target;
		this.autoGrowNestedPaths = autoGrowNestedPaths;
		this.autoGrowCollectionLimit = autoGrowCollectionLimit;
	}
//...

现在获得了bindingResult,属性如下:

  • 属性有objectName,target、errors(存放校验错误信息)、conversionService以及下面重要的**beanWrapper(BeanWrapperImpl)**等。

这里写图片描述



(11)—看一下getPropertyAccessor()的返回结果

class:beanPropertyBindingResult

@Override
	public final ConfigurablePropertyAccessor getPropertyAccessor() {
		if (this.beanWrapper == null) {
			this.beanWrapper = createBeanWrapper();
			this.beanWrapper.setExtractOldValueForEditor(true);
			this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
			this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit);
		}
		//看,最终返回的结果!!!
		return this.beanWrapper;
	}

看到上面,return this.beanWrapper !使用该对象进行属性设置!


回到⑦ void applyPropertyValues 开始为属性(propertyValues)赋值

如下图所示:

这里写图片描述


  • 如下图所示:把表单参数值,一一对应,赋值给propertyValue!

这里写图片描述



下面开始分析赋值流程

void setPropertyValues

@Override
	public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
			throws BeansException {

		List<PropertyAccessException> propertyAccessExceptions = null;
		List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
				((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));

		//循环遍历赋值循环遍历赋值循环遍历赋值
		for (PropertyValue pv : propertyValues) {
			try {
				// This method may throw any BeansException, which won't be caught
				// here, if there is a critical failure such as no matching field.
				// We can attempt to deal only with less serious exceptions.
				//调用具体赋值方法
				setPropertyValue(pv);
			}
			catch (NotWritablePropertyException ex) {
				if (!ignoreUnknown) {
					throw ex;
				}
				// Otherwise, just ignore it and continue...
			}
			catch (NullValueInNestedPathException ex) {
				if (!ignoreInvalid) {
					throw ex;
				}
				// Otherwise, just ignore it and continue...
			}
			catch (PropertyAccessException ex) {
				if (propertyAccessExceptions == null) {
					propertyAccessExceptions = new LinkedList<PropertyAccessException>();
				}
				propertyAccessExceptions.add(ex);
			}
		}

		// If we encountered individual exceptions, throw the composite exception.
		if (propertyAccessExceptions != null) {
			PropertyAccessException[] paeArray =
					propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
			throw new PropertyBatchUpdateException(paeArray);
		}
	}

(13) void setPropertyValue

  • 对具体属性赋值;
  • 期间会发生数据类型转换(比如gender的参数值为string,但是类型为Integer);
@Override
	public void setPropertyValue(PropertyValue pv) throws BeansException {
		PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
		if (tokens == null) {
			String propertyName = pv.getName();
			BeanWrapperImpl nestedBw;
			try {
			
			//拿到BeanWrapperImpl 
				nestedBw = getBeanWrapperForPropertyPath(propertyName);
			}
			catch (NotReadablePropertyException ex) {
				throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
						"Nested property in path '" + propertyName + "' does not exist", ex);
			}
			tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
			if (nestedBw == this) {
				pv.getOriginalPropertyValue().resolvedTokens = tokens;
			}
			//看这里!!!!为对应属性赋值!!!
			nestedBw.setPropertyValue(tokens, pv);
		}
		else {
			setPropertyValue(tokens, pv);
		}
	}


进入这个setPropertyValue方法

  • 赋值期间可能发生数据类型转换
BeanWrapperImpl.setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv)
private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {

//...
valueToApply = convertForProperty(propertyName, oldValue, originalValue, pd, new TypeDescriptor(property(pd)));
//...
//使用反射,为object 对应属性赋值(使用其set方法)
writeMethod.invoke(this.object, value);
}

这里写图片描述


    • 继续遍历赋值…


  • 为Gender赋值示例如下:

此时回到方法:void setPropertyValue((PropertyTokenHolder tokens, PropertyValue pv) )

class:void org.springframework.beans.BeanWrapperImpl

private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {

//...
writeMethod.invoke(this.object, value);
//...

使用反射进行赋值:

  • writeMethod :setGender;
  • object:Employee;
  • value:0;

如下图所示:

为gender赋值

这里写图片描述

  • 为gender赋值的时候发生了数据类型转换

这里写图片描述



此时回到方法void setPropertyValue(PropertyValue pv)

@Override
	public void setPropertyValue(PropertyValue pv) throws BeansException {
		PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
		if (tokens == null) {
			String propertyName = pv.getName();
			BeanWrapperImpl nestedBw;
			try {
				nestedBw = getBeanWrapperForPropertyPath(propertyName);
			}
			catch (NotReadablePropertyException ex) {
				throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
						"Nested property in path '" + propertyName + "' does not exist", ex);
			}
			tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
			if (nestedBw == this) {
				pv.getOriginalPropertyValue().resolvedTokens = tokens;
			}
			nestedBw.setPropertyValue(tokens, pv);
		}
		else {
			setPropertyValue(tokens, pv);
		}
	}

回到方法void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)

@Override
	public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
			throws BeansException {

		List<PropertyAccessException> propertyAccessExceptions = null;
		List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
				((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
		for (PropertyValue pv : propertyValues) {
			try {
				// This method may throw any BeansException, which won't be caught
				// here, if there is a critical failure such as no matching field.
				// We can attempt to deal only with less serious exceptions.
				setPropertyValue(pv);
			}
			catch (NotWritablePropertyException ex) {
				if (!ignoreUnknown) {
					throw ex;
				}
				// Otherwise, just ignore it and continue...
			}
			catch (NullValueInNestedPathException ex) {
				if (!ignoreInvalid) {
					throw ex;
				}
				// Otherwise, just ignore it and continue...
			}
			catch (PropertyAccessException ex) {
				if (propertyAccessExceptions == null) {
					propertyAccessExceptions = new LinkedList<PropertyAccessException>();
				}
				propertyAccessExceptions.add(ex);
			}
		}

		// If we encountered individual exceptions, throw the composite exception.
		if (propertyAccessExceptions != null) {
			PropertyAccessException[] paeArray =
					propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
			throw new PropertyBatchUpdateException(paeArray);
		}
	}


赋值完毕!!! 赋值完毕!!! 赋值完毕!!! 赋值完毕!!!


回到方法Object resolveArgument

	@Override
	public final Object resolveArgument(
			MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest request, WebDataBinderFactory binderFactory)
			throws Exception {

		String name = ModelFactory.getNameForParameter(parameter);
		Object attribute = (mavContainer.containsAttribute(name)) ?
				mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);

		WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
		if (binder.getTarget() != null) {
			//已经使用bindingResult里面的beanWrapper为Java bean object绑定了请求中的参数--Java bean初始化完毕!!!
			bindRequestParameters(binder, request);
			//开始校验!!!
			validateIfApplicable(binder, parameter);
			if (binder.getBindingResult().hasErrors()) {
				if (isBindExceptionRequired(binder, parameter)) {
					throw new BindException(binder.getBindingResult());
				}
			}
		}

开始校验!!!开始校验!!! 开始校验!!!开始校验!!!


这里写图片描述


查看此时的binder:

  • binder:ExtendedServletRequestDataBinder;
  • bindingResult

这里写图片描述


  • 完整的binder如下:

这里写图片描述


【请注意一个细节】:

binder中的target的id与 BindingResult中的target的id 一致!!!这说明各自属性引用的target对象是同一个!!

因为target是一个对象,在创建bindingResult使用了binder的target进行创建。此处应用的传递是引用传递,那么,BindingResult对target进行操作的时候,binder中的target也会改变!!!


校验方法void validateIfApplicable(WebDataBinder binder, MethodParameter parameter)

protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
		Annotation[] annotations = parameter.getParameterAnnotations();
		for (Annotation annot : annotations) {
			if (annot.annotationType().getSimpleName().startsWith("Valid")) {
				Object hints = AnnotationUtils.getValue(annot);
				binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
				break;
			}
		}
	}

真正开始校验真正开始校验真正开始校验真正开始校验!!!

validata方法注释:

Validate the supplied target object, which must be of a type of Class for which the supports(Class) method typically returns true. 

/*校验target*/

The supplied errors instance can be used to report any resulting validation errors. 

/*Erros 的子类实例可以存储校验错误*/

/* 该方法意思是校验target对象,并且把错误放在bindingresult里面!!!*/

Errors 接口:

/* Stores and exposes information about data-binding and validationerrors for a specific object.
*/
public interface Errors {}

BindingResult接口:

/**
 * General interface that represents binding results. Extends the
 * {@link Errors interface} for error registration capabilities,
 * allowing for a {@link Validator} to be applied, and adds
 * binding-specific analysis and model building.
 *
 * <p>Serves as result holder for a {@link DataBinder}, obtained via
 * the {@link DataBinder#getBindingResult()} method. BindingResult
 * implementations can also be used directly, for example to invoke
 * a {@link Validator} on it (e.g. as part of a unit test).
 */
public interface BindingResult extends Errors {}
public void validate(Object... validationHints) {
		for (Validator validator : getValidators()) {
			if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
				((SmartValidator) validator).validate(getTarget(), getBindingResult(), validationHints);
			}
			else if (validator != null) {
				validator.validate(getTarget(), getBindingResult());
			}
		}
	}

  • getTarget():从binder中获取target;
  • getBindingResult():从binder中获取bindingResult;

validate方法如下

@Override
	@SuppressWarnings("rawtypes")
	public void validate(Object target, Errors errors, Object... validationHints) {
		Set<Class> groups = new LinkedHashSet<Class>();
		if (validationHints != null) {
			for (Object hint : validationHints) {
				if (hint instanceof Class) {
					groups.add((Class) hint);
				}
			}
		}
		processConstraintViolations(
				this.targetValidator.validate(target, groups.toArray(new Class[groups.size()])), errors);
	}

processConstraintViolations方法说明如下图所示:

这里写图片描述


这里写图片描述


校验完毕!!!校验完毕!!!校验完毕!!!校验完毕!!!

  • 此时的binder与bindingResult:**

这里写图片描述

  • bindingResult中errors为0

回到解析参数的方法-Object resolveArgument

—终于回来了!!!

@Override
	public final Object resolveArgument(
			MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest request, WebDataBinderFactory binderFactory)
			throws Exception {

		String name = ModelFactory.getNameForParameter(parameter);
		Object attribute = (mavContainer.containsAttribute(name)) ?
				mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);

		WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
		if (binder.getTarget() != null) {
			bindRequestParameters(binder, request);
			validateIfApplicable(binder, parameter);

			/*从这里开始执行,看校验有没有错误!!!*/
			if (binder.getBindingResult().hasErrors()) {
				if (isBindExceptionRequired(binder, parameter)) {
					throw new BindException(binder.getBindingResult());
				}
			}
		}

		/*Add resolved attribute and BindingResult at the end of the model*/
		/*从BindingResult中拿到model---因为校验错误信息也在bindingResult里面;之所以不从binder对象里面获取,是因为前者更方便,一次将普通model与errors都获取出来*/
		
		Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
		
		/*{employee=Employee [id=null, lastName=aa, [email protected], gender=0, department=Department [id=101, departmentName=null], birth=null, salary=null], 
		//第一个参数结束org.springframework.validation.BindingResult.employee=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
		//如果参数有result的子类型,可以拿到校验的error
*/
		mavContainer.removeAttributes(bindingResultModel);
		mavContainer.addAllAttributes(bindingResultModel);
		/*这两个方法的目的是,如果原先mav有和bindingResultModel同样的key,就移除掉;然后重新添加bindingResultModel于mav容器中*/
		
		return binder.getTarget();
		/*返回target!!!注意,并没有返回Error,需要目标方法参数中注册Error errors*/
	}


  • 从这里获取bindingResultModel
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
  • 具体BeanPropertyBindingResult.getModel方法:

代码注释已经很清晰了,所以该语句过后,代码中的model涵盖了普通model和errors(key为MODEL_KEY_PREFIX + getObjectName())

@Override
	public Map<String, Object> getModel() {
		Map<String, Object> model = new LinkedHashMap<String, Object>(2);
		// Mapping from name to target object.
		model.put(getObjectName(), getTarget());
		// Errors instance, even if no errors.
		model.put(MODEL_KEY_PREFIX + getObjectName(), this);
		return model;
	}

这里写图片描述

  • 父类为AbstractBindingResult:
AbstractBindingResult extends AbstractErrors implements BindingResult, Serializable{}


此时返回的target !!!

这里写图片描述


回到HandlerMethodArgumentResolverComposite.resolveArgument

/**
	 * Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
	 * @exception IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
	 */
	@Override
	public Object resolveArgument(
			MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
			throws Exception {

		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}

再次返回,回到Object[] getMethodArgumentValues

第一个参数解析结束!!!

这里写图片描述


该此过程是先以Employee参数类型构建了一个空的Employee对象,使用binder将表单中的参数绑定到空的Employee对象的对应属性上,进行数据格式化和校验。。。最终返回binder的target对象!!!!即,属性赋值后的Employee!!!



第二参数开始解析!!!

这里写图片描述


  • 第二个参数类型为Errors,解析器如下:

这里写图片描述


  • ErrorsMethodArgumentResolver的解析方法如下

  • 即拿到保存校验错误的model.get(key)(key为MODEL_KEY_PREFIX+objectName);

org.springframework.validation.BeanPropertyBindingResult: 0 errors
	@Override
	public Object resolveArgument(
			MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
			throws Exception {
			
		/*从mav中获取model*/
		ModelMap model = mavContainer.getModel();
		//尝试获取errors_map
		if (model.size() > 0) {
			int lastIndex = model.size()-1;
			String lastKey = new ArrayList<String>(model.keySet()).get(lastIndex);
			if (lastKey.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
				return model.get(lastKey);
			}
		}

		throw new IllegalStateException(
				"An Errors/BindingResult argument is expected to be declared immediately after the model attribute, " +
				"the @RequestBody or the @RequestPart arguments to which they apply: " + parameter.getMethod());
	}

}

第二个参数解析结束!!!

这里写图片描述



第三个参数开始解析!!!

  • 因为第三个参数是map类型,故解析器为MapMethodProcessor

这里写图片描述

第三个参数解析结束!!!

  • 第三个参数默认类型为BindingAwareModelMap,有两个对象,一个Employee,另外一个对象为如下形式:
org.springframework.validation.BindingResult.employee=org.springframework.validation.BeanPropertyBindingResult: 0 errors}]

同时可以说明,bindingresult 数据也是放在了modelmap中,Employee数据也是放在了modelmap中,故第三个参数类型为map时,将会获得其所有model!!!—因为其最终都对应一个defaultModel–BindingAwareModelMap!!

这里写图片描述


至此,参数全部解析结束!!!


返回到Object invokeForRequest

public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			StringBuilder sb = new StringBuilder("Invoking [");
			sb.append(this.getBeanType().getSimpleName()).append(".");
			sb.append(getMethod().getName()).append("] method with arguments ");
			sb.append(Arrays.asList(args));
			logger.trace(sb.toString());
		}
		Object returnValue = invoke(args);
		if (logger.isTraceEnabled()) {
			logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
		}
		return returnValue;
	}

  • 此时args如下:

这里写图片描述


执行物理目标方法 save()!

  • 注意:return “forward:/emps”

这里写图片描述

  • 拿到返回值:

这里写图片描述


回到void invokeAndHandle()方法

① 拿到返回值;
② 处理返回值;

这里写图片描述


开始处理返回值

此时的handler:ViewNameMethodReturnValueHandler

① 拿到处理返回值对应的handler;

@Override
	public void handleReturnValue(
			Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws Exception {

		HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
		Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}

② 使用对应的handler处理返回值;

public void handleReturnValue(
			Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws Exception {

		if (returnValue == null) {
			return;
		}
		else if (returnValue instanceof String) {
			//转换为string
			String viewName = (String) returnValue;
			//为mav设置视图名字为mav设置视图名字为mav设置视图名字
			mavContainer.setViewName(viewName);
			if (isRedirectViewName(viewName)) {
				mavContainer.setRedirectModelScenario(true);
			}
		}
		else {
			// should not happen
			throw new UnsupportedOperationException("Unexpected return type: " +
					returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
		}
	}

此时的mav:

ModelAndViewContainer: reference to view with name 'forward:/emps';

 default model {employee=Employee [id=1006, lastName=aa, [email protected], gender=0, department=Department [id=101, departmentName=D-AA], birth=null, salary=null], 

org.springframework.validation.BindingResult.employee=org.springframework.validation.BeanPropertyBindingResult: 0 errors}

处理完目标方法和返回值了!!!处理完目标方法和返回值了!!!


回到方法ModelAndView invokeHandleMethod

可以决定最终返回的ModelAndView了!!!

  • 在多个地方为mav赋值model;
  • 在执行物理目标方法时,为mav设置了viewName;

这里写图片描述


	private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
		
		//先更新了modelFactory里面的model
		/*Synchronize model attributes with the session. Add BindingResult attributes where necessary.*/
		modelFactory.updateModel(webRequest, mavContainer);

		if (mavContainer.isRequestHandled()) {
			return null;
		}
		//重新获取model
		ModelMap model = mavContainer.getModel();
		//根据重新获取的model,生成新的mav;
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
		//如果mav没有视图关联,就设置一个view;
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
		//返回mav
		return mav;
	}

方法void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer)

public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
		
		//如果session失效了,移除sessionAttributes---在这里使用了sessionStatus属性!!!
		if (mavContainer.getSessionStatus().isComplete()){
			this.sessionAttributesHandler.cleanupAttributes(request);
		}
		else {
			/*Store a subset of the given attributes in the session. Attributes not declared as session attributes via @SessionAttributes are ignored.*/
			this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
		}
		//如果请求不是直接处理的---在这里使用了requestHandled属性!!!
		if (!mavContainer.isRequestHandled()) {
			//注意这里--Add BindingResult attributes to the model for attributes that require it.
			updateBindingResult(request, mavContainer.getModel());
		}
	}

方法updateBindingResult方法updateBindingResult方法updateBindingResult

方法注释:Add BindingResult attributes to the model for attributes that require it.

解释:为容器中的(key : value)对象,添加必要的BindingResult!!!如Employee,则需要BindingResult;Map等简单类型,则不需要!!!
----这就是最后更新model的意义所在。例如方法参数中有Errors类型的参数,将直接从mav.getModel.get(bindingResultKey)获取其对应的value

—可能会说,解析参数类型为Employee时,已经在defaultModel放入了 普通key value对象和BindingResult;
但是解析完参数的时候,就调用了目标物理方法,model中的值可能改变,如手动添加(“user”,user)对象进入model,而Java bean object 一般需要校验需要BindingResult!!故需要重新更新,以使视图拿到最新的mav进行渲染!!!

private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
		//拿到 key的列表
		List<String> keyNames = new ArrayList<String>(model.keySet());
		//循环遍历每一个name
		for (String name : keyNames) {
			//从model拿到值
			Object value = model.get(name);
			
			//Whether the given attribute requires a BindingResult in the model.如类似于Employee的参数就判断为需要
			if (isBindingCandidate(name, value)) {
				//拼接获得bindingResultKey,带有其name属性 
				String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
				
				//如果model没有这个bindingResultKey对应的属性---添加!!!有了就跳过
				if (!model.containsAttribute(bindingResultKey)) {
					//根据request、value、name创建databinder 以使dataBinder.getBindingResult()使用!!
					WebDataBinder dataBinder = binderFactory.createBinder(request, value, name);
					//又更新了一次model!!!
					model.put(bindingResultKey, dataBinder.getBindingResult());
				}
			}
		}
	}

那么问题来了,那些类型的name value需要BindingResult哪些不需要呢?


/**
	 * Whether the given attribute requires a {@link BindingResult} in the model.
	 */
	private boolean isBindingCandidate(String attributeName, Object value) {
		if (attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
			return false;
		}

		Class<?> attrType = (value != null) ? value.getClass() : null;
		if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, attrType)) {
			return true;
		}

		return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&
				!(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
	}
//集合不行,map不行,简单类型不行!!!

回到ModelAndView getModelAndView

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
		//此时已经更新过了model!!!
		modelFactory.updateModel(webRequest, mavContainer);

		if (mavContainer.isRequestHandled()) {
			return null;
		}
		//拿到最新最新的model
		ModelMap model = mavContainer.getModel();
		//创建最新最新的mav
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
		//可以返回了!!!
		return mav;
	}

返回方法ModelAndView invokeHandleMethod

return getModelAndView(mavContainer, modelFactory, webRequest);

**返回到方法ModelAndView handleInternal **

这里写图片描述


返回到方法ModelAndView handle

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return handleInternal(request, response, (HandlerMethod) handler);
	}

执行完拦截器的后置方法,开始处理转发结果

这里写图片描述


渲染视图

将viewName:“forward:/epms” reference to URL “/emps”!

这里写图片描述



…转发到list方法,过程不再赘述。。。…转发到list方法,过程不再赘述。。


需要注意的是,转发到list的过程中,首先同样执行@ModelAttribute注解的方法getEmployee!!!


list方法的返回值!!!


这里写图片描述


最终的最终的最终,返回最后的最后的最后的MV!!!


渲染视图

这里写图片描述

解析视图

  • 配置的两个视图解析器

这里写图片描述

生成View

这里写图片描述

此时的view 如下图所示:

这里写图片描述


真正开始渲染视图

这里写图片描述

。。。。下面的不再赘述!。。。。

猜你喜欢

转载自blog.csdn.net/J080624/article/details/58041191
今日推荐