/**
  * Resolve the argument from the model or if not found instantiate it with its default if it is
  * available. The model attribute is then populated with request values via data binding and
  * optionally validated if {@code @java.validation.Valid} is present on the argument.
  *
  * @throws org.springframework.validation.BindException if data binding and validation result in
  *     an error and the next method parameter is not of type {@link
  *     org.springframework.validation.Errors}.
  * @throws Exception if WebDataBinder initialization fails.
  */
 public final Object resolveArgument(
     MethodParameter parameter,
     ModelAndViewContainer mavContainer,
     NativeWebRequest request,
     WebDataBinderFactory binderFactory)
     throws Exception {
   String name = parameter.getParameterAnnotation(FormModel.class).value();
   Object target =
       (mavContainer.containsAttribute(name))
           ? mavContainer.getModel().get(name)
           : createAttribute(name, parameter, binderFactory, request);
   WebDataBinder binder = binderFactory.createBinder(request, target, name);
   target = binder.getTarget();
   if (target != null) {
     bindRequestParameters(mavContainer, binderFactory, binder, request, parameter);
     validateIfApplicable(binder, parameter);
     if (binder.getBindingResult().hasErrors()) {
       if (isBindExceptionRequired(binder, parameter)) {
         throw new BindException(binder.getBindingResult());
       }
     }
   }
   target = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType());
   mavContainer.addAttribute(name, target);
   return target;
 }
  /**
   * Resolve the argument from the model or if not found instantiate it with its default if it is
   * available. The model attribute is then populated with request values via data binding and
   * optionally validated if {@code @java.validation.Valid} is present on the argument.
   *
   * @throws BindException if data binding and validation result in an error and the next method
   *     parameter is not of type {@link Errors}.
   * @throws Exception if WebDataBinder initialization fails.
   */
  @Override
  public final Object resolveArgument(
      MethodParameter parameter,
      ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest,
      WebDataBinderFactory binderFactory)
      throws Exception {

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

    WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
    if (binder.getTarget() != null) {
      bindRequestParameters(binder, webRequest);
      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

    Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
    mavContainer.removeAttributes(bindingResultModel);
    mavContainer.addAllAttributes(bindingResultModel);

    return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
  }
  private void doBind(
      WebDataBinder binder, NativeWebRequest webRequest, boolean validate, boolean failOnErrors)
      throws Exception {

    doBind(binder, webRequest);
    if (validate) {
      binder.validate();
    }
    if (failOnErrors && binder.getBindingResult().hasErrors()) {
      throw new BindException(binder.getBindingResult());
    }
  }
 protected void validateComponent(WebDataBinder binder, MethodParameter parameter)
     throws BindException {
   boolean validateParameter = validateParameter(parameter);
   Annotation[] annotations = binder.getTarget().getClass().getAnnotations();
   for (Annotation annot : annotations) {
     if (annot.annotationType().getSimpleName().startsWith("Valid") && validateParameter) {
       Object hints = AnnotationUtils.getValue(annot);
       binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
     }
   }
   if (binder.getBindingResult().hasErrors()) {
     if (isBindExceptionRequired(binder, parameter)) {
       throw new BindException(binder.getBindingResult());
     }
   }
 }
  private ModelAndView getModelAndViewSettingsPage(
      Map<String, String> parameters, SettingsForm settingsForm, MockHttpServletRequest request) {
    if (settingsForm == null) {
      settingsForm = new SettingsForm();
    }

    WebDataBinder binder = new WebDataBinder(settingsForm, "settingsform");

    if (parameters != null) {
      request.setParameters(parameters);
      binder.bind(new MutablePropertyValues(request.getParameterMap()));
    }

    SessionStatus status = new SimpleSessionStatus();
    return controller.submitSettingsPage(settingsForm, binder.getBindingResult(), status, request);
  }
 private void validate(WebDataBinder binder, MethodParameter parameter)
     throws MethodArgumentNotValidException {
   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});
       BindingResult bindingResult = binder.getBindingResult();
       if (bindingResult.hasErrors()) {
         if (isBindingErrorFatal(parameter)) {
           throw new MethodArgumentNotValidException(parameter, bindingResult);
         }
       }
     }
   }
 }
  public final void updateModelAttributes(
      Object handler,
      Map<String, Object> mavModel,
      ExtendedModelMap implicitModel,
      NativeWebRequest webRequest)
      throws Exception {

    if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
      for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
        this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
      }
    }

    // Expose model attributes as session attributes, if required.
    // Expose BindingResults for all attributes, making custom editors available.
    Map<String, Object> model = (mavModel != null ? mavModel : implicitModel);
    if (model != null) {
      try {
        String[] originalAttrNames = model.keySet().toArray(new String[model.size()]);
        for (String attrName : originalAttrNames) {
          Object attrValue = model.get(attrName);
          boolean isSessionAttr =
              this.methodResolver.isSessionAttribute(
                  attrName, (attrValue != null ? attrValue.getClass() : null));
          if (isSessionAttr) {
            if (this.sessionStatus.isComplete()) {
              implicitModel.put(MODEL_KEY_PREFIX_STALE + attrName, Boolean.TRUE);
            } else if (!implicitModel.containsKey(MODEL_KEY_PREFIX_STALE + attrName)) {
              this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
            }
          }
          if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX)
              && (isSessionAttr || isBindingCandidate(attrValue))) {
            String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
            if (mavModel != null && !model.containsKey(bindingResultKey)) {
              WebDataBinder binder = createBinder(webRequest, attrValue, attrName);
              initBinder(handler, attrName, binder, webRequest);
              mavModel.put(bindingResultKey, binder.getBindingResult());
            }
          }
        }
      } catch (InvocationTargetException ex) {
        // User-defined @InitBinder method threw an exception...
        ReflectionUtils.rethrowException(ex.getTargetException());
      }
    }
  }
 public Object resolveArgument(
     MethodParameter parameter,
     ModelAndViewContainer mavContainer,
     NativeWebRequest webRequest,
     WebDataBinderFactory binderFactory)
     throws Exception {
   Object arg = readWithMessageConverters(webRequest, parameter, parameter.getParameterType());
   if (shouldValidate(parameter, arg)) {
     String argName = Conventions.getVariableNameForParameter(parameter);
     WebDataBinder binder = binderFactory.createBinder(webRequest, arg, argName);
     binder.validate();
     Errors errors = binder.getBindingResult();
     if (errors.hasErrors()) {
       throw new RequestBodyNotValidException(errors);
     }
   }
   return arg;
 }
  @SuppressWarnings("unchecked")
  public final void updateModelAttributes(
      Object handler,
      Map<String, Object> mavModel,
      ExtendedModelMap implicitModel,
      NativeWebRequest webRequest)
      throws Exception {

    if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
      for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
        this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
      }
    }

    // Expose model attributes as session attributes, if required.
    // Expose BindingResults for all attributes, making custom editors available.
    Map<String, Object> model = (mavModel != null ? mavModel : implicitModel);
    for (String attrName : new HashSet<String>(model.keySet())) {
      Object attrValue = model.get(attrName);
      boolean isSessionAttr =
          this.methodResolver.isSessionAttribute(
              attrName, (attrValue != null ? attrValue.getClass() : null));
      if (isSessionAttr && !this.sessionStatus.isComplete()) {
        this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
      }
      if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX)
          && (isSessionAttr || isBindingCandidate(attrValue))) {
        String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
        if (mavModel != null && !model.containsKey(bindingResultKey)) {
          WebDataBinder binder = createBinder(webRequest, attrValue, attrName);
          initBinder(handler, attrName, binder, webRequest);
          mavModel.put(bindingResultKey, binder.getBindingResult());
        }
      }
    }
  }
 /**
  * {@inheritDoc}
  *
  * <p>Downcast {@link org.springframework.web.bind.WebDataBinder} to {@link
  * org.springframework.web.bind.ServletRequestDataBinder} before binding.
  *
  * @throws Exception
  * @see org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory
  */
 protected void bindRequestParameters(
     ModelAndViewContainer mavContainer,
     WebDataBinderFactory binderFactory,
     WebDataBinder binder,
     NativeWebRequest request,
     MethodParameter parameter)
     throws Exception {
   Map<String, Boolean> hasProcessedPrefixMap = new HashMap<String, Boolean>();
   Class<?> targetType = binder.getTarget().getClass();
   ServletRequest servletRequest = prepareServletRequest(binder.getTarget(), request, parameter);
   WebDataBinder simpleBinder = binderFactory.createBinder(request, null, null);
   if (Collection.class.isAssignableFrom(targetType)) { // bind collection or array
     Type type = parameter.getGenericParameterType();
     Class<?> componentType = Object.class;
     Collection target = (Collection) binder.getTarget();
     List targetList = new ArrayList(target);
     if (type instanceof ParameterizedType) {
       componentType = (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0];
     }
     if (parameter.getParameterType().isArray()) {
       componentType = parameter.getParameterType().getComponentType();
     }
     for (Object key : servletRequest.getParameterMap().keySet()) {
       String prefixName = getPrefixName((String) key);
       // 每个prefix 只处理一次
       if (hasProcessedPrefixMap.containsKey(prefixName)) {
         continue;
       } else {
         hasProcessedPrefixMap.put(prefixName, Boolean.TRUE);
       }
       if (isSimpleComponent(prefixName)) { // bind simple type
         Map<String, Object> paramValues =
             WebUtils.getParametersStartingWith(servletRequest, prefixName);
         Matcher matcher = INDEX_PATTERN.matcher(prefixName);
         if (!matcher.matches()) { // 处理如 array=1&array=2的情况
           for (Object value : paramValues.values()) {
             targetList.add(simpleBinder.convertIfNecessary(value, componentType));
           }
         } else { // 处理如 array[0]=1&array[1]=2的情况
           int index = Integer.valueOf(matcher.group(1));
           if (targetList.size() <= index) {
             growCollectionIfNecessary(targetList, index);
           }
           targetList.set(
               index, simpleBinder.convertIfNecessary(paramValues.values(), componentType));
         }
       } else { // 处理如
         // votes[1].title=votes[1].title&votes[0].title=votes[0].title&votes[0].id=0&votes[1].id=1
         Object component = null;
         // 先查找老的 即已经在集合中的数据(而不是新添加一个)
         Matcher matcher = INDEX_PATTERN.matcher(prefixName);
         if (!matcher.matches()) {
           throw new IllegalArgumentException(
               "bind collection error, need integer index, key:" + key);
         }
         int index = Integer.valueOf(matcher.group(1));
         if (targetList.size() <= index) {
           growCollectionIfNecessary(targetList, index);
         }
         Iterator iterator = targetList.iterator();
         for (int i = 0; i < index; i++) {
           iterator.next();
         }
         component = iterator.next();
         if (component == null) {
           component = BeanUtils.instantiate(componentType);
         }
         WebDataBinder componentBinder = binderFactory.createBinder(request, component, null);
         component = componentBinder.getTarget();
         if (component != null) {
           ServletRequestParameterPropertyValues pvs =
               new ServletRequestParameterPropertyValues(servletRequest, prefixName, "");
           componentBinder.bind(pvs);
           validateIfApplicable(componentBinder, parameter);
           if (componentBinder.getBindingResult().hasErrors()) {
             if (isBindExceptionRequired(componentBinder, parameter)) {
               throw new BindException(componentBinder.getBindingResult());
             }
           }
           targetList.set(index, component);
         }
       }
       target.clear();
       target.addAll(targetList);
     }
   } else if (MapWapper.class.isAssignableFrom(targetType)) {
     Type type = parameter.getGenericParameterType();
     Class<?> keyType = Object.class;
     Class<?> valueType = Object.class;
     if (type instanceof ParameterizedType) {
       keyType = (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0];
       valueType = (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[1];
     }
     MapWapper mapWapper = ((MapWapper) binder.getTarget());
     Map target = mapWapper.getInnerMap();
     if (target == null) {
       target = new HashMap();
       mapWapper.setInnerMap(target);
     }
     for (Object key : servletRequest.getParameterMap().keySet()) {
       String prefixName = getPrefixName((String) key);
       // 每个prefix 只处理一次
       if (hasProcessedPrefixMap.containsKey(prefixName)) {
         continue;
       } else {
         hasProcessedPrefixMap.put(prefixName, Boolean.TRUE);
       }
       Object keyValue = simpleBinder.convertIfNecessary(getMapKey(prefixName), keyType);
       if (isSimpleComponent(prefixName)) { // bind simple type
         Map<String, Object> paramValues =
             WebUtils.getParametersStartingWith(servletRequest, prefixName);
         for (Object value : paramValues.values()) {
           target.put(keyValue, simpleBinder.convertIfNecessary(value, valueType));
         }
       } else {
         Object component = target.get(keyValue);
         if (component == null) {
           component = BeanUtils.instantiate(valueType);
         }
         WebDataBinder componentBinder = binderFactory.createBinder(request, component, null);
         component = componentBinder.getTarget();
         if (component != null) {
           ServletRequestParameterPropertyValues pvs =
               new ServletRequestParameterPropertyValues(servletRequest, prefixName, "");
           componentBinder.bind(pvs);
           validateComponent(componentBinder, parameter);
           target.put(keyValue, component);
         }
       }
     }
   } else { // bind model
     ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
     servletBinder.bind(servletRequest);
   }
 }
  @SuppressWarnings("unchecked")
  private Object[] resolveHandlerArguments(
      Method handlerMethod,
      Object handler,
      NativeWebRequest webRequest,
      ExtendedModelMap implicitModel)
      throws Exception {

    Class[] paramTypes = handlerMethod.getParameterTypes();
    Object[] args = new Object[paramTypes.length];

    for (int i = 0; i < args.length; i++) {
      MethodParameter methodParam = new MethodParameter(handlerMethod, i);
      methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
      GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
      String paramName = null;
      String headerName = null;
      boolean requestBodyFound = false;
      String cookieName = null;
      String pathVarName = null;
      String attrName = null;
      boolean required = false;
      String defaultValue = null;
      boolean validate = false;
      int found = 0;
      Annotation[] paramAnns = methodParam.getParameterAnnotations();

      for (Annotation paramAnn : paramAnns) {
        if (RequestParam.class.isInstance(paramAnn)) {
          RequestParam requestParam = (RequestParam) paramAnn;
          paramName = requestParam.value();
          required = requestParam.required();
          defaultValue = requestParam.defaultValue();
          found++;
        } else if (RequestHeader.class.isInstance(paramAnn)) {
          RequestHeader requestHeader = (RequestHeader) paramAnn;
          headerName = requestHeader.value();
          required = requestHeader.required();
          defaultValue = requestHeader.defaultValue();
          found++;
        } else if (RequestBody.class.isInstance(paramAnn)) {
          requestBodyFound = true;
          found++;
        } else if (CookieValue.class.isInstance(paramAnn)) {
          CookieValue cookieValue = (CookieValue) paramAnn;
          cookieName = cookieValue.value();
          required = cookieValue.required();
          defaultValue = cookieValue.defaultValue();
          found++;
        } else if (PathVariable.class.isInstance(paramAnn)) {
          PathVariable pathVar = (PathVariable) paramAnn;
          pathVarName = pathVar.value();
          found++;
        } else if (ModelAttribute.class.isInstance(paramAnn)) {
          ModelAttribute attr = (ModelAttribute) paramAnn;
          attrName = attr.value();
          found++;
        } else if (Value.class.isInstance(paramAnn)) {
          defaultValue = ((Value) paramAnn).value();
        } else if ("Valid".equals(paramAnn.annotationType().getSimpleName())) {
          validate = true;
        }
      }

      if (found > 1) {
        throw new IllegalStateException(
            "Handler parameter annotations are exclusive choices - "
                + "do not specify more than one such annotation on the same parameter: "
                + handlerMethod);
      }

      if (found == 0) {
        Object argValue = resolveCommonArgument(methodParam, webRequest);
        if (argValue != WebArgumentResolver.UNRESOLVED) {
          args[i] = argValue;
        } else if (defaultValue != null) {
          args[i] = resolveDefaultValue(defaultValue);
        } else {
          Class paramType = methodParam.getParameterType();
          if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
            args[i] = implicitModel;
          } else if (SessionStatus.class.isAssignableFrom(paramType)) {
            args[i] = this.sessionStatus;
          } else if (Errors.class.isAssignableFrom(paramType)) {
            throw new IllegalStateException(
                "Errors/BindingResult argument declared "
                    + "without preceding model attribute. Check your handler method signature!");
          } else if (BeanUtils.isSimpleProperty(paramType)) {
            paramName = "";
          } else {
            attrName = "";
          }
        }
      }

      if (paramName != null) {
        args[i] =
            resolveRequestParam(
                paramName, required, defaultValue, methodParam, webRequest, handler);
      } else if (headerName != null) {
        args[i] =
            resolveRequestHeader(
                headerName, required, defaultValue, methodParam, webRequest, handler);
      } else if (requestBodyFound) {
        args[i] = resolveRequestBody(methodParam, webRequest, handler);
      } else if (cookieName != null) {
        args[i] =
            resolveCookieValue(
                cookieName, required, defaultValue, methodParam, webRequest, handler);
      } else if (pathVarName != null) {
        args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
      } else if (attrName != null) {
        WebDataBinder binder =
            resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
        boolean assignBindingResult =
            (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
        if (binder.getTarget() != null) {
          doBind(binder, webRequest, validate, !assignBindingResult);
        }
        args[i] = binder.getTarget();
        if (assignBindingResult) {
          args[i + 1] = binder.getBindingResult();
          i++;
        }
        implicitModel.putAll(binder.getBindingResult().getModel());
      }
    }

    return args;
  }
  @Override
  public Object resolveArgument(
      MethodParameter parameter,
      ModelAndViewContainer mavContainer,
      NativeWebRequest request,
      WebDataBinderFactory binderFactory)
      throws Exception {

    HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
    assertIsMultipartRequest(servletRequest);

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

    String partName = getPartName(parameter);
    Object arg;

    if (MultipartFile.class.equals(parameter.getParameterType())) {
      Assert.notNull(
          multipartRequest,
          "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
      arg = multipartRequest.getFile(partName);
    } else if (isMultipartFileCollection(parameter)) {
      Assert.notNull(
          multipartRequest,
          "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
      arg = multipartRequest.getFiles(partName);
    } else if (isMultipartFileArray(parameter)) {
      Assert.notNull(
          multipartRequest,
          "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
      List<MultipartFile> files = multipartRequest.getFiles(partName);
      arg = files.toArray(new MultipartFile[files.size()]);
    } else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) {
      assertIsMultipartRequest(servletRequest);
      arg = servletRequest.getPart(partName);
    } else if (isPartCollection(parameter)) {
      assertIsMultipartRequest(servletRequest);
      arg = new ArrayList<Object>(servletRequest.getParts());
    } else if (isPartArray(parameter)) {
      assertIsMultipartRequest(servletRequest);
      arg = RequestPartResolver.resolvePart(servletRequest);
    } else {
      try {
        HttpInputMessage inputMessage =
            new RequestPartServletServerHttpRequest(servletRequest, partName);
        arg = readWithMessageConverters(inputMessage, parameter, parameter.getParameterType());
        WebDataBinder binder = binderFactory.createBinder(request, arg, partName);
        if (arg != null) {
          validate(binder, parameter);
        }
        mavContainer.addAttribute(
            BindingResult.MODEL_KEY_PREFIX + partName, binder.getBindingResult());
      } catch (MissingServletRequestPartException ex) {
        // handled below
        arg = null;
      }
    }

    RequestPart annot = parameter.getParameterAnnotation(RequestPart.class);
    boolean isRequired = (annot == null || annot.required());

    if (arg == null && isRequired) {
      throw new MissingServletRequestPartException(partName);
    }

    return arg;
  }