這一篇,將着手介紹一次請求的處理。用到了 HandlerMapping、HandlerAdapter 知識,若是遇到不是太瞭解,能夠回顧下。java
其實 DispatcherServlet 也只是 Servlet 的一個實現,只不過它集成了 SpringMVC 的幾個功能組件(例如視圖解析器),對請求及響應進行加工處理,因此探索一個 Servlet 實現,先從它的 service 方法實現開始,來看下 javax.servlet.http.HttpServlet 的實現。web
public abstract class HttpServlet extends GenericServlet { @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest) req; response = (HttpServletResponse) res; } catch (ClassCastException e) { throw new ServletException("non-HTTP request or response"); } // 向下轉型 service(request, response); } protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); // 根據請求方式調用不一樣的 doXxx方法 if (method.equals(METHOD_GET)) { // 固定返回-1,說明緩存機制交由子類擴展 long lastModified = getLastModified(req); if (lastModified == -1) { doGet(req, resp); } else { /** * 緩存機制:首次請求後添加 「Last-Modefied」 的響應頭, * 第二次請求發送請求頭 If-Modified-Since * 若是服務端內容沒有變化,則自動返回 304狀態碼 */ long ifModifiedSince; try { ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); } catch (IllegalArgumentException iae) { ifModifiedSince = -1; } if (ifModifiedSince < (lastModified / 1000 * 1000)) { maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req, resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req, resp); } else { // 對於不支持請求方式,經過響應流輸出錯誤 String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } } }
能夠看到 service 就是作了向下轉型,調用了 service(HttpServletRequest req, HttpServletResponse resp)。而後在裏面根據請求方式,調用不一樣的 doXxx 方法。以上屬於 servlet-api 自身的實現,接下來看看 SpringMVC 如何在此基礎上改造。api
// HttpServletBean是 HttpServlet子類 public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { // 覆蓋 service方法 @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); // 支持 Patch請求或沒有指定請求方法 if (httpMethod == HttpMethod.PATCH || httpMethod == null) { // 無論什麼請求,都會調用公共的處理方法 processRequest(request, response); } else { super.service(request, response); } } @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 請求開始時間,用於計算請求耗時 long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); // 經過"accept-language"請求頭構造 LocaleContext LocaleContext localeContext = buildLocaleContext(request); // 獲取當前線程請求的 RequestAttributes RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); // 異步請求管理器 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor( FrameworkServlet.class.getName(), new RequestBindingInterceptor()); // 將 LocaleContext、ServletRequestAttributes與當前線程綁定 initContextHolders(request, localeContext, requestAttributes); try { // 該類定義的抽象方法,子類 DispatcherServlet實現 doService(request, response); } .....// 省略 catch處理 finally{ resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } .....// 省略日誌 publishRequestHandledEvent(request, response, startTime, failureCause); } } // 發佈 ServletRequestHandledEvent事件 private void publishRequestHandledEvent( HttpServletRequest request, HttpServletResponse response, long startTime, Throwable failureCause) { // 默認爲 true if (this.publishEvents) { // 請求耗時 long processingTime = System.currentTimeMillis() - startTime; // 響應碼 int statusCode = (responseGetStatusAvailable ? response.getStatus() : -1); // 封裝了 請求的 url、remoteAddr、請求方式、sessionId、處理時間、響應碼等信息 this.webApplicationContext.publishEvent( new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(), request.getMethod(), getServletConfig().getServletName(), WebUtils.getSessionId(request), getUsernameForRequest(request), processingTime, failureCause, statusCode)); } } }
除了支持 servlet 自身支持的 7 種請求外,另外支持了 PATCH 方式請求。這裏只是列舉了 doGet、doPost,其實最終都調用了 processRequest 方法。跨域
processRequest 方法經過調用 doService 來處理請求,在處理結束後發佈了 ServletRequestHandledEvent 事件,能夠自定義 ApplicationListener 來監聽此事件。緩存
public class DispatcherServlet extends FrameworkServlet { @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { .....// 省略日誌 // 若是 attributes含有 「javax.servlet.include.request_uri」,保留屬性的快照 // 用於支持 <jsp:incluede> Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<>(); // 獲取屬性枚舉 Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { // 將屬性壓入map中 attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // 設置上下文、解析器等屬性 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); // 取出上一個請求的 FlashMap並給賦值給當前請求 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); try { // 關注此方法 doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // 還原屬性快照 if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } } }
DispatcherServlet 來進行主要的處理實現。doService 進行一些屬性的設置以後,調用 doDispatch 方法進行處理session
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { /** * 調用咱們配置的 MultipartResolver判斷是否須要處理,若是沒配置不會處理 * 以 CommonsMultipartResolver爲例 * 檢測 contentType是否爲 multipart/form-data且必須是 POST請求 */ processedRequest = checkMultipart(request); // 若是知足上述條件,會包裝成 DefaultMultipartHttpServletRequest // 因此 multipartRequestParsed 爲 true multipartRequestParsed = (processedRequest != request); // 根據 request找到對應的 Handler mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { // 沒找到 Handler,經過response響應 404錯誤信息 noHandlerFound(processedRequest, response); return; } // 根據 Handler找到對應的適配器 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 對 last-modified 頭支持:只有 Get 和 Head請求支持 String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); ....// 省略日誌 // 對於緩存邏輯的判斷,見 ServletWebRequest.checkNotModified if (new ServletWebRequest(request, response). checkNotModified(lastModified) && isGet) { // 知足條件直接返回 return; } } // 調用攔截器的 preHandle,返回true才能經過 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 調用 HandlerAdapter.handle返回處理後的 ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // 若是沒有 ModelAndView返回,則根據請求默認給出一個視圖名稱 applyDefaultViewName(processedRequest, mv); // 調用攔截器的 postHandle mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { dispatchException = new NestedServletException("Handler dispatch failed", err); } // 視圖處理(包含異常視圖),最後會調用攔截器的 triggerAfterCompletion processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { // 調用攔截器的 triggerAfterCompletion triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { // 調用攔截器的 triggerAfterCompletion triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null) { // 調用 AsyncHandlerInterceptor.afterConcurrentHandlingStarted // 用於在異步請求處理開始以後回調 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // 若是是文件上傳請求,須要清理資源 if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
經過層層的調用,終於來到了 doDispatch ,這個方法大體可以看到請求的處理步驟概覽:app
接下來咱們來看幾個主要步驟的解析。框架
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { // 遍歷全部註冊的 HandlerMapping for (HandlerMapping hm : this.handlerMappings) { ....// 省略日誌 // 找到匹配的HandlerExecutionChain(不爲null) HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } } return null; }
調用 HandlerMapping.getHandler 獲取請求對應的 Handler。來看實現:異步
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered { @Override @Nullable public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 根據 request獲取對應的 Handler(子類實現) Object handler = getHandlerInternal(request); // 若是沒有找到對應 Handler,使用默認Handler if (handler == null) { handler = getDefaultHandler(); } // 若是默認的 Handler沒有,則返回 null if (handler == null) { return null; } // Handler爲 String類型,說明是beanName,調用 getBean獲取 if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } // 執行鏈構造:Handler和攔截器 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); // 判斷跨域請求:是否帶有「Origin」請求頭 if (CorsUtils.isCorsRequest(request)) { // 全局跨域配置 CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request); // 單個 Handler配置(註解 @CrossOrigin其實就是對單個 Handler的配置) CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); // 配置合併 CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); // 添加 CorsInterceptor攔截器(使用合併後的配置) executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; } // 執行鏈構造 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); // 遍歷 List<HandlerInterceptor> for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { // 將匹配的 MappedInterceptor加入處理鏈 chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { // 其餘的直接加入 chain.addInterceptor(interceptor); } } return chain; } // 跨域攔截器加入 protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request, HandlerExecutionChain chain, CorsConfiguration config) { // 條件:帶有「Origin」、「Access-Control-Request-Method」請求頭的 options請求 if (CorsUtils.isPreFlightRequest(request)) { HandlerInterceptor[] interceptors = chain.getInterceptors(); // 使用 PreFlightHandler替代本來的 Handler處理請求 chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors); } else { // 添加 CorsInterceptor攔截器(攔截器末尾) chain.addInterceptor(new CorsInterceptor(config)); } return chain; } }
抽象父類 AbstractHandlerMapping 實現了 執行鏈的構造 以及 「跨域」相關處理(攔截器),查找 Handler 的邏輯交由子類實現(getHandlerInternal)。回想一下 HandlerMapping 註冊 Handler 的邏輯分爲了兩個分支 AbstractUrlHandlerMapping 和 AbstractHandlerMethodMapping (見 HandlerMapping 初始化),所以查找邏輯也隨註冊邏輯不一樣而不一樣。jsp
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping { @Override protected Object getHandlerInternal(HttpServletRequest request) throws Exception { // 這一步咱們會取到截取後的請求相對地址 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); // 根據相對地址找到對應的 Handler Object handler = lookupHandler(lookupPath, request); if (handler == null) { // 沒有的話,依次查詢根 Handler、默認 Handler Object rawHandler = null; if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // 若是 Handler是 beanName,調用 getBean獲取 if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = getApplicationContext().getBean(handlerName); } // 校驗:由 DefaultAnnotationHandlerMapping實現 // 看請求是否知足 @RequestMapping指定的 method、param、header validateHandler(rawHandler, request); // 使用 Handler建立執行鏈,鏈頭添加 PathExposingHandlerInterceptor攔截器 handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } ....// 省略日誌 return handler; } protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { // 從映射關係中找出請求對應的 Handler(直接路徑) // 映射關係的初始化見 HandlerMapping初始化 Object handler = this.handlerMap.get(urlPath); if (handler != null) { // beanName以及 Handler校驗,上面講過了 if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); // 構造執行鏈 return buildPathExposingHandler(handler, urlPath, urlPath, null); } // 直接路徑中未找到,使用通配符匹配 List<String> matchingPatterns = new ArrayList<String>(); for (String registeredPattern : this.handlerMap.keySet()) { if (getPathMatcher().match(registeredPattern, urlPath)) { matchingPatterns.add(registeredPattern); } else if (useTrailingSlashMatch()) { if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) { matchingPatterns.add(registeredPattern + "/"); } } } String bestMatch = null; // 使用模式匹配後,查找最匹配的 Handler Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath); if (!matchingPatterns.isEmpty()) { Collections.sort(matchingPatterns, patternComparator); ....// 省略日誌 bestMatch = matchingPatterns.get(0); } if (bestMatch != null) { handler = this.handlerMap.get(bestMatch); if (handler == null) { if (bestMatch.endsWith("/")) { handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1)); } if (handler == null) { throw new IllegalStateException( "Could not find handler for best pattern match [" + bestMatch + "]"); } } // beanName以及 Handler校驗,上面講過了 if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath); // 可能存在多個「最佳模式」,讓咱們確保全部這些模式都有正確的URI模板變量 Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>(); for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestMatch, matchingPattern) == 0) { Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars); uriTemplateVariables.putAll(decodedVars); } } ....// 省略日誌 // 構造執行鏈 return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables); } // 沒有匹配的 Handler返回 null return null; } }
首先調用 UrlPathHelper.getLookupPathForRequest 獲取請求的相對路徑。以 Tomcat 舉例,配置的 <Context path="xxx"> 項目根路徑,那麼對應的 web 應用全部的請求,都要添加 「xxx」 前綴,但咱們的應用對此是無感知的,因此框架層面要把這些截取後,再去查找 Handler。來看下截取邏輯:
public class UrlPathHelper { public String getLookupPathForRequest(HttpServletRequest request) { // 默認爲 false if (this.alwaysUseFullPath) { return getPathWithinApplication(request); } String rest = getPathWithinServletMapping(request); if (!"".equals(rest)) { return rest; } else { return getPathWithinApplication(request); } } public String getPathWithinServletMapping(HttpServletRequest request) { // 獲取截取後的相對地址 String pathWithinApp = getPathWithinApplication(request); // 獲取的 <servlet>指定的 <url-pattern>(移除通配符後) String servletPath = getServletPath(request); // 把 pathWithinApp中的「//」替換成「/」 String sanitizedPathWithinApp = getSanitizedPath(pathWithinApp); String path; // 這一步主要就是對請求地址中多餘的「/」進行移除匹配 if (servletPath.contains(sanitizedPathWithinApp)) { // 一樣的,把請求的相對地址中,servletPath截取掉 path = getRemainingPath(sanitizedPathWithinApp, servletPath, false); } else { // 一樣的,把請求的相對地址中,servletPath截取掉 path = getRemainingPath(pathWithinApp, servletPath, false); } if (path != null) { return path; } else { // 若是請求不在 servlet指定的 <url-pattern>下 String pathInfo = request.getPathInfo(); if (pathInfo != null) { return pathInfo; } if (!this.urlDecode) { path = getRemainingPath(decodeInternal(request, pathWithinApp), servletPath, false); if (path != null) { return pathWithinApp; } } return servletPath; } } public String getPathWithinApplication(HttpServletRequest request) { // 獲取的項目根路徑 String contextPath = getContextPath(request); // 獲取請求的相對地址 String requestUri = getRequestUri(request); // 把請求的相對地址中的項目根路徑截去 String path = getRemainingPath(requestUri, contextPath, true); if (path != null) { return (StringUtils.hasText(path) ? path : "/"); } else { return requestUri; } } }
(getPathWithinServletMapping)首先會在配置的 DispatcherServlet 範圍內查找,對於同一個請求 「http://localhost/a/b」 來講:
(getPathWithinApplication)只有在上一步返回空字符串時纔會在 Application 範圍內查找,對於用一個請求「http://localhost/context/a」 來講:
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean { @Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 一樣使用 UrlPathHelper.getLookupPathForRequest String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } this.mappingRegistry.acquireReadLock(); try { // 根據請求 url找到對應的 HandlerMethod HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); ....// 省略日誌 // createWithResolvedBean目的是確保 HandlerMethod中持有的是被調用的實例 bean // 而不是 beanName,若是是會調用了 getBean獲取實例後建立新的 HandleMethod // 由於這個實例 bean會用於反射調用方法 return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } } protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); // 使用 MappingRegistry.getMappingsByUrl,經過相對路徑查找對應的 List<RequestMappingInfo> // 見 AbstractHandlerMethodMapping的註冊邏輯 List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { // 不爲 null,說明 @RequestMapping指定的是非通配符路徑 // 找到匹配條件的填充 matches addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // 若是經過直接路徑找不到,就在全部註冊的映射路徑中查找 // 找到匹配條件的填充 matches addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } if (!matches.isEmpty()) { // 由於可能會有多個匹配方法,須要根據定義的優先級排序 Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); Collections.sort(matches, comparator); if (logger.isTraceEnabled()) { logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches); } // 取出最匹配的 Match bestMatch = matches.get(0); if (matches.size() > 1) { // 條件:帶有「Origin」、「Access-Control-Request-Method」請求頭的 options請求 if (CorsUtils.isPreFlightRequest(request)) { // 返回 EmptyHandler封裝的 HandlerMethod // 調用會拋出異常 UnsupportedOperationException return PREFLIGHT_AMBIGUOUS_MATCH; } // 有兩個相同優先級的匹配結果,會拋異常 Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" + m1 + ", " + m2 + "}"); } } // 調用 setAttribute設置一些屬性 handleMatch(bestMatch.mapping, lookupPath, request); // 返回匹配的 HandlerMethod return bestMatch.handlerMethod; } else { // 未匹配 return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } } }
一樣使用的是 UrlPathHelper.getLookupPathForRequest 獲取請求的相對路徑。以後根據相對路徑找到對應的 HandlerMethod 。涉及到了一個請求地址對應了多個匹配結果的篩選:
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) { for (T mapping : mappings) { // 獲取知足條件的 RequestMappingInfo T match = getMatchingMapping(mapping, request); // 只有知足返回的纔不爲 null,這一步會篩選掉不符合條件的 // 例如:請求路徑不匹配、header頭不匹配等等 if (match != null) { // 經過 MappingRegistry維護的 mappingLookup找到對應的 HandlerMethod // 使用 RequestMappingInfo和 HandlerMethod建立 Match // 用於 MatchComparator的排序 matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping))); } } }
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> { @Override protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) { // 調用 RequestMappingInfo.getMatchingCondition return info.getMatchingCondition(request); } }
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> { @Override public RequestMappingInfo getMatchingCondition(HttpServletRequest request) { RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request); ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request); HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request); ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request); ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request); // 這一步基本不會,由於 @RequestMapping都有默認值 if (methods == null || params == null || headers == null || consumes == null || produces == null) { return null; } // 對於 @RequestMapping指定的 path/value匹配(直接地址匹配、通配符匹配) PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request); if (patterns == null) { return null; } // 對於 @RequestMapping指定的 method、param、header、consumes、produces的匹配 RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request); if (custom == null) { return null; } return new RequestMappingInfo(this.name, patterns, methods, params, headers, consumes, produces, custom.getCondition()); } }
以上的源碼,就是用知足請求條件的(指定的 method、header等) RequestMappingInfo 以及對應的 HandlerMethod 封裝成 Match, 並填充到 matches 的邏輯。
這一步可能會篩選出多個匹配項,接下來就須要靠 MatchComparator 排序後挑選出最匹配項,邏輯見 RequestMappingInfo.compareTo,這裏不展開分析。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { // 遍歷全部註冊的 HandlerAdapter for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) { logger.trace("Testing handler adapter [" + ha + "]"); } // 調用 HandlerAdapter.supports看是否支持適配 if (ha.supports(handler)) { return ha; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
這裏調用的 supports 獲取到支持適配的 HandlerAdapter,以後調用其 handle 方法執行處理邏輯,具體源碼上篇已分析,見 「HandlerAdapter」。
執行完以後,就獲取到了 ModelAndView 返回,接着就是對視圖的處理(processDispatchResult),放在下節分析。
本篇分析的就是一個請求從接收處處理的全過程,目前進度已經獲取到了 ModelAndView,至於請求地址如何找到對應的 Handler、Handler 如何調用並返回 ModelAndView ,前幾篇已做講解。下篇將分析 「視圖處理」 源碼。