SpringMVC源碼解析(四)——請求處理

前言

    這一篇,將着手介紹一次請求的處理。用到了 HandlerMappingHandlerAdapter 知識,若是遇到不是太瞭解,能夠回顧下。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 來監聽此事件。緩存

 

doService

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

 

doDispatch

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

  • 首先判斷是否爲文件上傳的請求:和普通的請求不同,這種請求須要在請求結束後清理文件解析過程當中建立的 MultipartFile (可能會在磁盤上保留有臨時數據);
  • 獲取 Handler :從 HandlerMapping初始化創建的映射關係中找出;
  • 攔截器 HandlerInterceptor.preHandle 調用;
  • HandlerAdapter 適配:從 HandlerAdapter 初始化註冊的全部適配器中,找到支持對應 Handler 的適配器,調用 handle 方法處理(見 HandlerAdapter
  • 攔截器 HandlerInterceptor.postHandle 調用;
  • 調用 processDispatchResult 進行視圖處理
  • 攔截器 HandlerInterceptor.afterCompletion 調用(異常一樣會觸發,而且會做爲參數傳入)

    接下來咱們來看幾個主要步驟的解析。框架

 

getHandler

@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

 

AbstractUrlHandlerMapping 分支

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」 來講:

  • <url-pattern>/*</url-pattern>, 返回的就是 「/a/b」;
  • <url-pattern>/a/*</url-pattern>,那麼返回的就是 「/b」。若是請求爲 「http://localhost/a」 ,那麼返回的就是空字符串了。

    (getPathWithinApplication)只有在上一步返回空字符串時纔會在 Application 範圍內查找,對於用一個請求「http://localhost/context/a」 來講:

  • <Context path="/context">,返回的就是 「/a」;
  • <Context path="/">,返回的就是 「/context/a」。若是請求爲 「http://localhost」 ,那麼返回的就是 「/」了。

 

AbstractHandlerMethodMapping 分支

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,這裏不展開分析。

 

getHandlerAdapter

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 ,前幾篇已做講解。下篇將分析 「視圖處理」 源碼。

相關文章
相關標籤/搜索