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

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

編寫控制器方法的時候很奇怪,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一個例項,最後返回,完畢。