SpringMVC
来源:互联网 发布:华为荣耀盒子安装软件 编辑:程序博客网 时间:2024/05/22 03:04
上面讲述的是从 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 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()
【获取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; }
- 【通过HandlerExecutionChain获取handler从而得到handlerAdapter!】
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=jik@qq.com, 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=jik@qq.com, 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 如下图所示:
真正开始渲染视图
。。。。下面的不再赘述!。。。。
- SpringMVC
- springmvc
- SpringMVC
- SpringMVC
- springMvc
- springMVC
- springmvc
- springMVC
- springMVC
- springmvc
- SpringMVC
- SpringMVC
- springMvc
- springmvc
- springmvc
- SpringMVC
- Springmvc
- springmvc
- java对象与类小议
- git 的一系列操作
- Unity教程之-Unity Attribute的使用总结
- jdk1.8 concurrenthashmap
- 【Ubuntu 16】 SSH免密码登录
- SpringMVC
- 机器学习简介
- 函数式编程(1)
- JAVA深复制(深克隆)与浅复制(浅克隆)
- angularjs-路由
- 堆和栈的区别(被转载过无数次的文章)
- Mybatis 的分页插件PageHelper-4.1.1的使用
- 插件化知识详细分解及原理 之ClassLoader及dex加载过程
- jvm-监控指令-jstack