spring mvc中的控制器方法中的參數從哪裏傳進來?
阿新 • • 發佈:2018-11-22
ear use binding 再次 valid instant spring beanutils urn 參數理器對象,這些參數處理器都是不同的HandlerMethodArgumentResolver類的不同子類的實例。然後用循環,一個個測試這些參數處理器是否支持這個參數的類型,如果支持,就返回這個參數處理器,並用這個參數處理器的resolveArgument方法,該方法會返回一個合適的參數對象,這個參數對象是我們寫的控制方法參數的子類。比如上面showRegistrationForm(Model model)的參數對象是Model,那麽支持Model參數的參數處理器就是ModelAndViewContainer類的對象,然後然後這個參數處理器對象的resolveArgument方法會返回一個BindingAwareModelMap對象,這個BindingAwareModelMap對象正好是Model的子類,傳入showRegistrationForm(Model model) 中,我們就可以通過model操作這個對象了。
編寫控制器方法的時候很奇怪,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一個實例,最後返回,完畢。
spring mvc中的控制器方法中的參數從哪裏傳進來?