编写控制器方法的时候很奇怪,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 方法如下:
public final Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory binderFactory)
throws Exception { String name = ModelFactory.getNameForParameter(parameter);
Object attribute = (mavContainer.containsAttribute(name)) ?
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request); WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
if (binder.getTarget() != null) {
bindRequestParameters(binder, request);
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors()) {
if (isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
}
看山上面第8行,如果模型中没有这个属性对象,那么就会createAttribute创建这个属性,再看creat4eAttribute这个方法是怎么创建pojo对象的:
protected Object createAttribute(String attributeName, MethodParameter parameter,
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception { return BeanUtils.instantiateClass(parameter.getParameterType());
}
果然没错,就是用java的反射,根据对象的类型instance一个实例,最后返回,完毕。