1. 程式人生 > >spring mvc中的控制器方法中的參數從哪裏傳進來?

spring mvc中的控制器方法中的參數從哪裏傳進來?

ear use binding 再次 valid instant spring beanutils urn

編寫控制器方法的時候很奇怪,spring是怎麽知道你控制器方法的參數類型,並且註入正確的對象呢?

比如下面這樣

@RequestMapping(value="/register", method=GET)
  public String showRegistrationForm(Model model) {
    model.addAttribute(new Spitter());
    return "registerForm";
  }

他怎麽知道Model對應啥呢?

其實,spring首先會反射這個方法,然後獲得參數的類型,另外在spring中,保存著一系列的argumentResolvers

參數理器對象,這些參數處理器都是不同的HandlerMethodArgumentResolver類的不同子類的實例。然後用循環,一個個測試這些參數處理器是否支持這個參數的類型,如果支持,就返回這個參數處理器,並用這個參數處理器的resolveArgument方法,該方法會返回一個合適的參數對象,這個參數對象是我們寫的控制方法參數的子類。比如上面showRegistrationForm(Model model)的參數對象是Model,那麽支持Model參數的參數處理器就是ModelAndViewContainer類的對象,然後然後這個參數處理器對象的resolveArgument方法會返回一個BindingAwareModelMap對象,這個BindingAwareModelMap對象正好是Model的子類,傳入showRegistrationForm(Model model)
中,我們就可以通過model操作這個對象了。

這個循環檢測是在HandlerMethodArguementResolverComposite類的getArgumentResolver方法中進行的:

private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
        if (result == null
) { for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) { if (logger.isTraceEnabled()) { logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" + parameter.getGenericParameterType() + "]"); } if (methodArgumentResolver.supportsParameter(parameter)) { result = methodArgumentResolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; }

另外還可以看到,這個方法中用了緩存,這樣當以後再次進入這個控制器方法是,就直接從緩存中取得argumentResolver就可以了。

如果是控制器方法的參數是一個普通的pojo(不是ioc容器中管理的spring bean),比如對應一個表單的數據,比如下面這樣,應該怎麽處理呢?

  @RequestMapping(value="/register", method=POST)
  public String processRegistration(
      @Valid Spitter spitter, 
      Errors errors) {
    if (errors.hasErrors()) {
      return "registerForm";
    }
    
    spitterRepository.save(spitter);
    return "redirect:/spitter/" + spitter.getUsername();
  }

processRegistration的第一個參數是Spitter,他只是一個普通的自定義的pojo,那麽這個參數將被看作是一個模型屬性,並且用ModleAttributeMethodProcessor這個參數處理器來處理。ModleAttributeMethodProcessor的resolveArgument 方法如下:

 1 public final Object resolveArgument(
 2             MethodParameter parameter, ModelAndViewContainer mavContainer,
 3             NativeWebRequest request, WebDataBinderFactory binderFactory)
 4             throws Exception {
 5 
 6         String name = ModelFactory.getNameForParameter(parameter);
 7         Object attribute = (mavContainer.containsAttribute(name)) ?
 8                 mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
 9 
10         WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
11         if (binder.getTarget() != null) {
12             bindRequestParameters(binder, request);
13             validateIfApplicable(binder, parameter);
14             if (binder.getBindingResult().hasErrors()) {
15                 if (isBindExceptionRequired(binder, parameter)) {
16                     throw new BindException(binder.getBindingResult());
17                 }
18             }
19         }

看山上面第8行,如果模型中沒有這個屬性對象,那麽就會createAttribute創建這個屬性,再看creat4eAttribute這個方法是怎麽創建pojo對象的:

    protected Object createAttribute(String attributeName, MethodParameter parameter,
            WebDataBinderFactory binderFactory,  NativeWebRequest request) throws Exception {

        return BeanUtils.instantiateClass(parameter.getParameterType());
    }

果然沒錯,就是用java的反射,根據對象的類型instance一個實例,最後返回,完畢。

spring mvc中的控制器方法中的參數從哪裏傳進來?