@Test
  public void resolveArgumentOrdering() throws Exception {
    String name = "testBean";
    Object testBean = new TestBean(name);
    mavContainer.addAttribute(name, testBean);
    mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, testBean);

    Object anotherTestBean = new TestBean();
    mavContainer.addAttribute("anotherTestBean", anotherTestBean);

    StubRequestDataBinder dataBinder = new StubRequestDataBinder(testBean, name);
    WebDataBinderFactory binderFactory = createMock(WebDataBinderFactory.class);
    expect(binderFactory.createBinder(webRequest, testBean, name)).andReturn(dataBinder);
    replay(binderFactory);

    processor.resolveArgument(paramModelAttr, mavContainer, webRequest, binderFactory);

    assertSame(
        "Resolved attribute should be updated to be last in the order",
        testBean,
        mavContainer.getModel().values().toArray()[1]);
    assertSame(
        "BindingResult of resolved attribute should be last in the order",
        dataBinder.getBindingResult(),
        mavContainer.getModel().values().toArray()[2]);
  }
  @Test
  public void resovleArgumentViaDefaultConstructor() throws Exception {
    WebDataBinder dataBinder = new WebRequestDataBinder(null);

    WebDataBinderFactory factory = createMock(WebDataBinderFactory.class);
    expect(factory.createBinder((NativeWebRequest) anyObject(), notNull(), eq("attrName")))
        .andReturn(dataBinder);
    replay(factory);

    processor.resolveArgument(paramNamedValidModelAttr, mavContainer, webRequest, factory);

    verify(factory);
  }
  private void getAttributeFromModel(String expectedAttributeName, MethodParameter param)
      throws Exception {
    Object target = new TestBean();
    mavContainer.addAttribute(expectedAttributeName, target);

    WebDataBinder dataBinder = new WebRequestDataBinder(target);
    WebDataBinderFactory factory = createMock(WebDataBinderFactory.class);
    expect(factory.createBinder(webRequest, target, expectedAttributeName)).andReturn(dataBinder);
    replay(factory);

    processor.resolveArgument(param, mavContainer, webRequest, factory);

    verify(factory);
  }
  @Test(expected = BindException.class)
  public void resovleArgumentBindException() throws Exception {
    String name = "testBean";
    Object target = new TestBean();
    mavContainer.getModel().addAttribute(target);

    StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name);
    dataBinder.getBindingResult().reject("error");

    WebDataBinderFactory binderFactory = createMock(WebDataBinderFactory.class);
    expect(binderFactory.createBinder(webRequest, target, name)).andReturn(dataBinder);
    replay(binderFactory);

    processor.resolveArgument(paramNonSimpleType, mavContainer, webRequest, binderFactory);
  }
 /**
  * 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);
  }
  @Test
  public void resovleArgumentValidation() throws Exception {
    String name = "attrName";
    Object target = new TestBean();
    mavContainer.addAttribute(name, target);

    StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name);
    WebDataBinderFactory binderFactory = createMock(WebDataBinderFactory.class);
    expect(binderFactory.createBinder(webRequest, target, name)).andReturn(dataBinder);
    replay(binderFactory);

    processor.resolveArgument(paramNamedValidModelAttr, mavContainer, webRequest, binderFactory);

    assertTrue(dataBinder.isBindInvoked());
    assertTrue(dataBinder.isValidateInvoked());
  }
 /**
  * Create a model attribute from a String request value (e.g. URI template variable, request
  * parameter) using type conversion.
  *
  * <p>The default implementation converts only if there a registered {@link
  * org.springframework.core.convert.converter.Converter} that can perform the conversion.
  *
  * @param sourceValue the source value to setBasicInfoForCreate the model attribute from
  * @param attributeName the name of the attribute, never {@code null}
  * @param parameter the method parameter
  * @param binderFactory for creating WebDataBinder instance
  * @param request the current request
  * @return the created model attribute, or {@code null}
  * @throws Exception
  */
 protected Object createAttributeFromRequestValue(
     String sourceValue,
     String attributeName,
     MethodParameter parameter,
     WebDataBinderFactory binderFactory,
     NativeWebRequest request)
     throws Exception {
   DataBinder binder = binderFactory.createBinder(request, null, attributeName);
   ConversionService conversionService = binder.getConversionService();
   if (conversionService != null) {
     TypeDescriptor source = TypeDescriptor.valueOf(String.class);
     TypeDescriptor target = new TypeDescriptor(parameter);
     if (conversionService.canConvert(source, target)) {
       return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter);
     }
   }
   return null;
 }
 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;
 }
 /**
  * {@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);
   }
 }
  @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;
  }