咱們要使用定義了RequestMapping方法或者類是,須要先準備好所須要的參數。如何準備參數,咱們應該考慮些上面問題。html
除了方法肯定的參數,還有兩個方法的參數須要綁定,那就是當前處理器相對應註釋了@ModelAttribute和註釋了@InitBinder的方法。web
有六個參數的來源:緩存
參數解析使用HandlerMethodArgumentResolver類型的組件完成的,不一樣類型的使用不一樣的ArgumentResolver來解析。有的Resolver內部使用了WebDataBinder,能夠經過註釋了@InitBinder的方法來初始化,註釋了@InitBinder的方法也須要綁定參數,並且也是不肯定的,因此@InitBinder註釋的方法也須要ArgumentResolver來解析參數,使用的和Handler不一樣的一套ArgumentResolver,另外註釋了ModelAttribute的方法也須要綁定參數,使用的和Handler使用的是同一套ArgumentResolver。cookie
此類的主要功能:session
經過xml解析初始化過程如RequestMapping原理分析和RequestMappingHandlerMapping通常,重複部分就不在分析。app
上圖信息比較多,咱們查找關鍵信息。能夠看到這個類間接實現了HandlerAdapter接口,是HandlerAdapter類型的實例。ide
除此以外還實現了ApplicationContextAware和IntitalzingBean 這兩個接口。函數
既然RequestMappingHandlerAdapter實現了ApplicationContextAware接口,那實例化時候確定會執行setApplicationContext方法,咱們查看其實現邏輯。工具
@Override public final void setApplicationContext(ApplicationContext context) throws BeansException { //isContextRequired()方法返回tru if (context == null && !isContextRequired()) { // Reset internal context state. this.applicationContext = null; this.messageSourceAccessor = null; } else if (this.applicationContext == null) { // Initialize with passed-in context. //所傳入的context若是不能被實例化,則拋出異常 if (!requiredContextClass().isInstance(context)) { throw new ApplicationContextException( "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]"); } this.applicationContext = context; //國際化 this.messageSourceAccessor = new MessageSourceAccessor(context); //初始化ApplicationContext initApplicationContext(context); } else { // Ignore reinitialization if same context passed in. if (this.applicationContext != context) { throw new ApplicationContextException( "Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]"); } } }
initApplicationContext裏面主要就是初始化ServletContext,就不在去分析了源碼分析
RequestMappingHandlerAdapter也實現了InitializingBean接口,當設置完屬性後確定會回調afterPropertiesSet方法,咱們重點分析一下afterPropertiesSet的方法,它的源碼內容以下,下面咱們一點一點的分析這個類:
@Override public void afterPropertiesSet() { // Do this first, it may add ResponseBody advice beans //初始化註釋了@ControllerAdvice的類,分別用於緩存@ControllerAdvice註釋的類裏面註釋了@ModelAttribute和@InitBinder方法,也就是全局的@ModelAttribute和InitBinder方法。 initControllerAdviceCache(); if (this.argumentResolvers == null) { //初始化argumentResolvers,用於處理器方法和註釋了@ModelAttribute的方法設置參數 List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.initBinderArgumentResolvers == null) { //初始化initBinderArgumentResolvers,用於給註釋了@initBinder的方法設置參數,使用得較少 List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers(); this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.returnValueHandlers == null) { //初始化returnValueHandlers,用於將處理器的返回值處理爲ModelAndView的類型 List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); } }
咱們先看initControllerAdviceCache這個方法:
private void initControllerAdviceCache() { if (getApplicationContext() == null) { return; } if (logger.isInfoEnabled()) { logger.info("Looking for @ControllerAdvice: " + getApplicationContext()); } //獲取到全部註釋了@ControllerAdvice的bean List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); //對獲取到的ControllerAdvice註解的類進行排序,排序的規則是基於實現PriorityOrdered接口或者帶有Order註解 AnnotationAwareOrderComparator.sort(beans); List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>(); for (ControllerAdviceBean bean : beans) { //查找註釋了@ModelAttribute並且沒有註釋@RequestMapping的方法 Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS); if (!attrMethods.isEmpty()) { this.modelAttributeAdviceCache.put(bean, attrMethods); if (logger.isInfoEnabled()) { logger.info("Detected @ModelAttribute methods in " + bean); } } //獲取全部帶InitBinder註解的方法 Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS); if (!binderMethods.isEmpty()) { this.initBinderAdviceCache.put(bean, binderMethods); if (logger.isInfoEnabled()) { logger.info("Detected @InitBinder methods in " + bean); } } //若是實現了RequestBodyAdvice接口 if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) { requestResponseBodyAdviceBeans.add(bean); if (logger.isInfoEnabled()) { logger.info("Detected RequestBodyAdvice bean in " + bean); } } //若是實現了ResponseBodyAdvice接口 if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) { requestResponseBodyAdviceBeans.add(bean); if (logger.isInfoEnabled()) { logger.info("Detected ResponseBodyAdvice bean in " + bean); } } } // 將實現了RequestBodyAdvice和ResponseBodyAdvice接口的類放入requestResponseBodyAdviceBeans // 這裏是放入頂部,說明經過@@ControllerAdvice註解實現接口的處理優先級最高 if (!requestResponseBodyAdviceBeans.isEmpty()) { this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans); } }
看getDefaultArgumentResolvers主要做用是解析傳入的參數:
// 獲取默認的 HandlerMethodArgumentResolver private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); // 1.基於註解的參數解析 <-- 解析的數據來源主要是 HttpServletRequest | ModelAndViewContainer // Annotation-based argument resolution // 解析被註解 @RequestParam, @RequestPart 修飾的參數, 數據的獲取經過 HttpServletRequest.getParameterValues resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); // 解析被註解 @RequestParam 修飾, 且類型是 Map 的參數, 數據的獲取經過 HttpServletRequest.getParameterMap resolvers.add(new RequestParamMapMethodArgumentResolver()); // 解析被註解 @PathVariable 修飾, 數據的獲取經過 uriTemplateVars, 而 uriTemplateVars 倒是經過 RequestMappingInfoHandlerMapping.handleMatch 生成, 其實就是 uri 中映射出的 key <-> value resolvers.add(new PathVariableMethodArgumentResolver()); // 解析被註解 @PathVariable 修飾 且數據類型是 Map, 數據的獲取經過 uriTemplateVars, 而 uriTemplateVars 倒是經過 RequestMappingInfoHandlerMapping.handleMatch 生成, 其實就是 uri 中映射出的 key <-> value resolvers.add(new PathVariableMapMethodArgumentResolver()); // 解析被註解 @MatrixVariable 修飾, 數據的獲取經過 URI提取了;後存儲的 uri template 變量值 resolvers.add(new MatrixVariableMethodArgumentResolver()); // 解析被註解 @MatrixVariable 修飾 且數據類型是 Map, 數據的獲取經過 URI提取了;後存儲的 uri template 變量值 resolvers.add(new MatrixVariableMapMethodArgumentResolver()); // 解析被註解 @ModelAttribute 修飾, 且類型是 Map 的參數, 數據的獲取經過 ModelAndViewContainer 獲取, 經過 DataBinder 進行綁定 resolvers.add(new ServletModelAttributeMethodProcessor(false)); // 解析被註解 @RequestBody 修飾的參數, 以及被@ResponseBody修飾的返回值, 數據的獲取經過 HttpServletRequest 獲取, 根據 MediaType經過HttpMessageConverter轉換成對應的格式, 在處理返回值時 也是經過 MediaType 選擇合適HttpMessageConverter, 進行轉換格式, 並輸出 resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); // 解析被註解 @RequestPart 修飾, 數據的獲取經過 HttpServletRequest.getParts() resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); // 解析被註解 @RequestHeader 修飾, 數據的獲取經過 HttpServletRequest.getHeaderValues() resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); // 解析被註解 @RequestHeader 修飾且參數類型是 Map, 數據的獲取經過 HttpServletRequest.getHeaderValues() resolvers.add(new RequestHeaderMapMethodArgumentResolver()); // 解析被註解 @CookieValue 修飾, 數據的獲取經過 HttpServletRequest.getCookies() resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); // 解析被註解 @Value 修飾, 數據在這裏沒有解析 resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); // 解析被註解 @SessionAttribute 修飾, 數據的獲取經過 HttpServletRequest.getAttribute(name, RequestAttributes.SCOPE_SESSION) resolvers.add(new SessionAttributeMethodArgumentResolver()); // 解析被註解 @RequestAttribute 修飾, 數據的獲取經過 HttpServletRequest.getAttribute(name, RequestAttributes.SCOPE_REQUEST) resolvers.add(new RequestAttributeMethodArgumentResolver()); // 2.基於類型的參數解析器 // Type-based argument resolution // 解析固定類型參數(好比: ServletRequest, HttpSession, InputStream 等), 參數的數據獲取仍是經過 HttpServletRequest resolvers.add(new ServletRequestMethodArgumentResolver()); // 解析固定類型參數(好比: ServletResponse, OutputStream等), 參數的數據獲取仍是經過 HttpServletResponse resolvers.add(new ServletResponseMethodArgumentResolver()); // 解析固定類型參數(好比: HttpEntity, RequestEntity 等), 參數的數據獲取仍是經過 HttpServletRequest resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); // 解析固定類型參數(好比: RedirectAttributes), 參數的數據獲取仍是經過 HttpServletResponse resolvers.add(new RedirectAttributesMethodArgumentResolver()); // 解析固定類型參數(好比: Model等), 參數的數據獲取經過 ModelAndViewContainer resolvers.add(new ModelMethodProcessor()); // 解析固定類型參數(好比: Model等), 參數的數據獲取經過 ModelAndViewContainer resolvers.add(new MapMethodProcessor()); // 解析固定類型參數(好比: Errors), 參數的數據獲取經過 ModelAndViewContainer resolvers.add(new ErrorsMethodArgumentResolver()); // 解析固定類型參數(好比: SessionStatus), 參數的數據獲取經過 ModelAndViewContainer resolvers.add(new SessionStatusMethodArgumentResolver()); // 解析固定類型參數(好比: UriComponentsBuilder), 參數的數據獲取經過 HttpServletRequest resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); // 3.自定義參數解析器 // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all //這兩個解析器能夠解析全部類型的參數 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }
getDefaultArgumentResolvers中有四類解析器:
從源碼中能夠看出,自定義的解析器是在前面兩種都沒法解析是纔會使用到,這個順序是沒法改變的,例如若是想本身寫一個解析器來解析@PathVariable註釋的PathVariable參數,是沒法實現的,即便寫出來並註冊到RequestMappingHanderAdapter中也不會被調用
getDefaultInitBinderArgumentResolvers用得較少,原理也是相似的,就不分析了。
分析getDefaultReturnValueHandlers方法:
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>(); // Single-purpose return value types // 支持 ModelAndView 類型的 HandlerMethodReturnValueHandler, 最後將數據寫入 ModelAndViewContainer handlers.add(new ModelAndViewMethodReturnValueHandler()); // 支持 Map 類型的 HandlerMethodReturnValueHandler, 最後將數據寫入 ModelAndViewContainer handlers.add(new ModelMethodProcessor()); // 支持 View 類型的 HandlerMethodReturnValueHandler, 最後將數據寫入 ModelAndViewContainer handlers.add(new ViewMethodReturnValueHandler()); // 支持 ResponseEntity 類型的 HandlerMethodReturnValueHandler, 最後將數據寫入 HttpServletResponse 的數據流中 OutputStream handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters())); // 支持 ResponseEntity | StreamingResponseBody 類型的 HandlerMethodReturnValueHandler, 最後將數據寫入 HttpServletResponse 的數據流中 OutputStream handlers.add(new StreamingResponseBodyReturnValueHandler()); // 支持 HttpEntity | !RequestEntity 類型的 HandlerMethodReturnValueHandler, 最後將數據寫入 HttpServletResponse 的數據流中 OutputStream handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice)); // 支持 HttpHeaders 類型的 HandlerMethodReturnValueHandler, 最後將數據寫入 HttpServletResponse 的數據頭部 handlers.add(new HttpHeadersReturnValueHandler()); // 支持 Callable 類型的 HandlerMethodReturnValueHandler handlers.add(new CallableMethodReturnValueHandler()); handlers.add(new DeferredResultMethodReturnValueHandler()); // 支持 WebAsyncTask 類型的 HandlerMethodReturnValueHandler handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory)); // Annotation-based return value types // 將數據加入 ModelAndViewContainer 的 HandlerMethodReturnValueHandler handlers.add(new ModelAttributeMethodProcessor(false)); // 返回值被 ResponseBody 修飾的返回值, 而且根據 MediaType 經過 HttpMessageConverter 轉化後進行寫入數據流中 handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice)); // Multi-purpose return value types // 支持返回值爲 CharSequence 類型, 設置 ModelAndViewContainer.setViewName handlers.add(new ViewNameMethodReturnValueHandler()); // 支持返回值爲 Map, 並將結果設置到 ModelAndViewContainer handlers.add(new MapMethodProcessor()); // Custom return value types if (getCustomReturnValueHandlers() != null) { handlers.addAll(getCustomReturnValueHandlers()); } // Catch-all if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) { handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers())); } else { handlers.add(new ModelAttributeMethodProcessor(true)); } return handlers; }
在這裏就初始化完成了,有了這些工具類後咱們就能夠對收到請求的參數和處理後返回不一樣的類型進行處理了。
DispatchServlet繼承自Servlet,那全部的請求都會在service()方法中進行處理。最後在DispatcherServlet#doDispatch中處理請求後。根據不一樣的請求會找到對應的HandlerMapping,而後在找到HandlerAdapter進行處理。
若是處理的HandlerAdapter是RequestMappingHandlerAdapter,最後會走到RequestMappingHandlerAdapter#handleInternal:
@Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ...... mav = invokeHandlerMethod(request, response, handlerMethod); ...... }
進入invokeHandlerMethod:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { // 構建 ServletWebRequest <-- 主要由 HttpServletRequest, HttpServletResponse ServletWebRequest webRequest = new ServletWebRequest(request, response); try { // 構建 DataBinder 工廠 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); // binderFactory 中存儲着被 @InitBinder, @ModelAttribute 修飾的方法 <- 最終包裹成 InvocableHandlerMethod ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); // 構建一個 ServletInvocableHandlerMethod ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); // 設置方法參數解析器 HandlerMethodArgumentValueResolver invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); // 返回值處理器 HandlerMethodReturnValueHandler invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); // 設置 WebDataBinderFactory invocableMethod.setDataBinderFactory(binderFactory); // 設置 參數名解析器 invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); // 獲取 HttpServletRequest 中存儲的 FlashMap mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); // 這裏是激活 @ModelAttribute, @InitBinder 方法, 並將返回值放入 ModelAndViewContainer modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); // 將 HttpServletRequest 轉換成方法的參數, 激活方法, 最後 經過 HandlerMethodReturnValueHandler 來處理返回值 invocableMethod.invokeAndHandle(webRequest, mavContainer); // 生成 ModelAndView return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); // 標誌請求已經結束, 進行一些生命週期回調函數的激活 } }
在上面的代碼中咱們建立了一個ServletInvocableHandlerMethod對象,在這個對象中設置了參數解析器、返回值處理器、數據校驗工廠類等。接着咱們進入到invocableMethod.invokeAndHandle這個方法中看一下:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); ...... }
在invokeForRequest這個方法中,主要乾了兩件事,一是解析請求參數,二是調用Controller中的請求方法。這裏咱們主要關注的是參數解析的部分:
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //解析請求參數 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "' with arguments " + Arrays.toString(args)); } //調用Controller中的請求方法 Object returnValue = doInvoke(args); if (logger.isTraceEnabled()) { logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "] returned [" + returnValue + "]"); } return returnValue; }
進入getMethodArgumentValues:
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //這裏的 getMethodParameters 實際上是在構造 MethodParameters 時建立的 MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object[parameters.length]; //從這裏開始對參數進行一個一個解析 <- 主要是經過 HandlerMethodArgumentResolver for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); //若是以前有預先設置值的話,則取預先設置好的值 args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } //獲取能解析出方法參數值的參數解析器類 if (this.argumentResolvers.supportsParameter(parameter)) { try { //經過 argumentResolvers 解析 HandlerMethod 裏面對應的參數內容 args[i] = this.argumentResolvers.resolveArgument( parameter, mavContainer, request, this.dataBinderFactory); continue; } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex); } throw ex; } } //若是沒有能解析方法參數的類,拋出異常 if (args[i] == null) { throw new IllegalStateException("Could not resolve method parameter at index " + parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() + ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i)); } } return args; }
這裏須要說的是argumentResolvers這個對象是HandlerMethodArgumentResolverComposite這個類。全部參數的解析都是委託這個類來完成的,這個類會調用真正的請求參數的解析的類:
@Override public boolean supportsParameter(MethodParameter parameter) { return (getArgumentResolver(parameter) != null); }
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; }
下一篇分析具體從URL參數中傳的值,如何綁定到對應controller參數上面。