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