spring mvc中的控制器方法中的引數從哪裡傳進來?
阿新 • • 發佈:2018-11-21
編寫控制器方法的時候很奇怪,spring是怎麼知道你控制器方法的引數型別,並且注入正確的物件呢?
比如下面這樣
@RequestMapping(value="/register", method=GET) public String showRegistrationForm(Model model) { model.addAttribute(new Spitter()); return "registerForm"; }
他怎麼知道Model對應啥呢?
其實,spring首先會反射這個方法,然後獲得引數的型別,另外在spring中,儲存著一系列的argumentResolvers
這個迴圈檢測是在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一個例項,最後返回,完畢。