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一个实例,最后返回,完毕。

 

posted on 2018-11-21 22:37  J.M.Liu  阅读(432)  评论(0编辑  收藏  举报