Spring MVC原始碼(二) ----- DispatcherServlet 請求處理流程 面試必問

chen_hao發表於2019-07-16

前端控制器

前端控制器,即所謂的Front Controller,體現的是設計模式中的前端控制器模式。前端控制器處理所有從使用者過來的請求。所有使用者的請求都要通過前端控制器。SpringMVC框架和其他請求驅動的表示層框架一樣,也是圍繞一個將請求分發到相應控制器的核心Servlet來設計的。DispatcherServlet和其他框架中的Servlet不一樣的地方在於,它和Spring容器無縫整合在了一起,因此你可以在SpringMVC中使用Spring容器所有的特性。

DispatcherServlet這個前端控制器,在SpringMVC中的作用,以官方文件中的配圖來說明:

整個流程可以被大致描述為:一個http請求到達伺服器,被DispatcherServlet接收。DispatcherServlet將請求委派給合適的處理器Controller,此時處理控制權到達Controller物件。Controller內部完成請求的資料模型的建立和業務邏輯的處理,然後再將填充了資料後的模型即model和控制權一併交還給DispatcherServlet,委派DispatcherServlet來渲染響應。DispatcherServlet再將這些資料和適當的資料模版檢視結合,向Response輸出響應。

DispatcherServlet

SpringMVC完成初始化流程之後,就進入Servlet標準生命週期的第二個階段,即“service”階段。在“service”階段中,每一次Http請求到來,容器都會啟動一個請求執行緒,通過service()方法,委派到doGet()或者doPost()這些方法,完成Http請求的處理。

在初始化流程中,SpringMVC巧妙的運用依賴注入讀取引數,並最終建立一個與容器上下文相關聯的Spring子上下文。這個子上下文,就像Struts2中xwork容器一樣,為接下來的Http處理流程中各種程式設計元素提供了容身之所。如果說將Spring上下文關聯到Servlet容器中,是SpringMVC框架的第一個亮點,那麼在請求轉發流程中,SpringMVC對各種處理環節程式設計元素的抽象,就是另外一個獨具匠心的亮點。

Struts2採取的是一種完全和Web容器隔離和解耦的事件機制。諸如Action物件、Result物件、Interceptor物件,這些都是完全脫離Servlet容器的程式設計元素。Struts2將資料流和事件處理完全剝離開來,從Http請求中讀取資料後,下面的事件處理流程就只依賴於這些資料,而完全不知道有Web環境的存在。

反觀SpringMVC,無論HandlerMapping物件、HandlerAdapter物件還是View物件,這些核心的介面所定義的方法中,HttpServletRequest和HttpServletResponse物件都是直接作為方法的引數出現的。這也就意味著,框架的設計者,直接將SpringMVC框架和容器繫結到了一起。或者說,整個SpringMVC框架,都是依託著Servlet容器元素來設計的。下面就來看一下,原始碼中是如何體現這一點的。

請求轉發的入口

就像任何一個註冊在容器中的Servlet一樣,DispatcherServlet也是通過自己的service()方法來接收和轉發Http請求到具體的doGet()或doPost()這些方法的。以一次典型的GET請求為例,經過HttpServlet基類中service()方法的委派,請求會被轉發到doGet()方法或者doPost()方法中。

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getMethod();
    long lastModified;
    if (method.equals("GET")) {
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
            this.doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader("If-Modified-Since");
            if (ifModifiedSince < lastModified) {
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } else {
                resp.setStatus(304);
            }
        }
    } else if (method.equals("HEAD")) {
        lastModified = this.getLastModified(req);
        this.maybeSetLastModified(resp, lastModified);
        this.doHead(req, resp);
    } else if (method.equals("POST")) {
        this.doPost(req, resp);
    } else if (method.equals("PUT")) {
        this.doPut(req, resp);
    } else if (method.equals("DELETE")) {
        this.doDelete(req, resp);
    } else if (method.equals("OPTIONS")) {
        this.doOptions(req, resp);
    } else if (method.equals("TRACE")) {
        this.doTrace(req, resp);
    } else {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[]{method};
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
    }

}

doGet() 和 doPost() 方法,在DispatcherServlet的父類FrameworkServlet類中被覆寫。

protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.processRequest(request, response);
}

protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.processRequest(request, response);
}

可以看到,這裡只是簡單的轉發到processRequest()這個方法。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;

    // Expose current LocaleResolver and request as LocaleContext.
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);

    // Expose current RequestAttributes to current thread.
    RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = null;
    if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) {
        requestAttributes = new ServletRequestAttributes(request);
        RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
    }

    if (logger.isTraceEnabled()) {
        logger.trace("Bound request context to thread: " + request);
    }

    try {
        doService(request, response);
    }
    finally {
        // Clear request attributes and reset thread-bound context.
        LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
        if (requestAttributes != null) {
            RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
            requestAttributes.requestCompleted();
        }
        if (this.publishEvents) {
            // Whether or not we succeeded, publish an event.
            long processingTime = System.currentTimeMillis() - startTime;
            this.webApplicationContext.publishEvent(
                    new ServletRequestHandledEvent(this,
                            request.getRequestURI(), request.getRemoteAddr(),
                            request.getMethod(), getServletConfig().getServletName(),
                            WebUtils.getSessionId(request), getUsernameForRequest(request),
                            processingTime, failureCause));
        }
    }
}

可以看到,processRequest()方法只是做了一些執行緒安全的隔離,真正的請求處理,發生在doService()方法中。點開FrameworkServlet類中的doService()方法。

protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
        throws Exception;

又是一個抽象方法,這也是SpringMVC類設計中的慣用伎倆:父類抽象處理流程,子類給予具體的實現。真正的實現是在DispatcherServlet類中。

讓我們接著看DispatcherServlet類中實現的doService()方法。

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (logger.isDebugEnabled()) {
        String requestUri = urlPathHelper.getRequestUri(request);
        logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
                " request for [" + requestUri + "]");
    }

    // Keep a snapshot of the request attributes in case of an include,
    // to be able to restore the original attributes after the include.
    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
        logger.debug("Taking snapshot of request attributes before include");
        attributesSnapshot = new HashMap<String, Object>();
        Enumeration<?> attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }

    // Make framework objects available to handlers and view objects.
    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 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 {
        // Restore the original attribute snapshot, in case of an include.
        if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
        }
    }
}

幾個requet.setAttribute()方法的呼叫,將前面在初始化流程中例項化的物件設定到http請求的屬性中,供下一步處理使用,其中有容器的上下文物件、本地化解析器等SpringMVC特有的程式設計元素。不同於Struts2中的ValueStack,SpringMVC的資料並沒有從HttpServletRequest物件中抽離出來再存進另外一個程式設計元素,這也跟SpringMVC的設計思想有關。因為從一開始,SpringMVC的設計者就認為,不應該將請求處理過程和Web容器完全隔離

所以,你可以看到,真正發生請求轉發的方法doDispatch()中,它的引數是HttpServletRequest和HttpServletResponse物件。這給我們傳遞的意思也很明確,從request中能獲取到一切請求的資料,從response中,我們又可以往伺服器端輸出任何響應,Http請求的處理,就應該圍繞這兩個物件來設計。我們不妨可以將SpringMVC這種設計方案,是從Struts2的過度設計中吸取教訓,而向Servlet程式設計的一種迴歸和簡化。

而對請求的處理交給doDispatcher方法

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 {
            // 處理檔案上傳
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // 決定當前請求的Handler
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // 決定當前請求的HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 處理last-modified請求頭
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (logger.isDebugEnabled()) {
                    logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                }
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            // 攔截器的前置處理
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // Handler實際執行請求
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            // 設定預設檢視名
            applyDefaultViewName(processedRequest, mv);
            // 攔截器後置處理
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }

        // 選擇檢視並渲染檢視
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                new NestedServletException("Handler processing failed", err));
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}

先看doDispatcher方法執行的主要操作時序圖

請求路由

getHandler方法就是從HandlerMapping中查詢匹配當前request的Handler。我們看到只要一匹配上 handler 就不再迴圈,直接返回

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    for (HandlerMapping hm : this.handlerMappings) {
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}

HandlerMapping的getHandler方法在抽象基類AbstractHandlerMapping

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 由子類根據request獲取Handler
    Object handler = getHandlerInternal(request);
    // 如果沒匹配到,則獲取預設Handler
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    // 如果返回的Handler為String,則使用Spring容器例項化
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
    }

    // 查詢匹配的攔截器,組裝Handler生成HandlerExecutionChain
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    if (CorsUtils.isCorsRequest(request)) {
        CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }
    return executionChain;
}

最終返回的Handler是由攔截器鏈和Handler共同組成的,而具體匹配Handler的方法是交給子類來完成的。上一章元件初始化中提到生產環境下使用的是RequestMappingHandlerMapping,getHandlerInternal方法的實現在它的基類AbstractHandlerMethodMapping。

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 從request獲取匹配url
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    if (logger.isDebugEnabled()) {
        logger.debug("Looking up handler method for path " + lookupPath);
    }
    this.mappingRegistry.acquireReadLock();
    try {
        // 查詢匹配的HandlerMethod
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        if (logger.isDebugEnabled()) {
            if (handlerMethod != null) {
                logger.debug("Returning handler method [" + handlerMethod + "]");
            }
            else {
                logger.debug("Did not find handler method for [" + lookupPath + "]");
            }
        }
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}

可以看到返回的Handler的型別為HandlerMethod,它對應於Controller中的方法。上一章也提過,在AbstractHandlerMethodMapping中有一個MappingRegistry,統一管理URL和Controller方法的對映關係,lookupHandlerMethod就是對MappingRegistry的操作。

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<Match>();
    // 從mappingRegistry獲取匹配到的RequestMappingInfo
    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    if (directPathMatches != null) {
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        // No choice but to go through all mappings...
        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) {
            if (CorsUtils.isPreFlightRequest(request)) {
                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 + "}");
            }
        }
        handleMatch(bestMatch.mapping, lookupPath, request);
        return bestMatch.handlerMethod;
    }
    else {
        // 無匹配項處理
        return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    }
}

通過mappingRegistry匹配返回RequestMappingInfo,對應於每個有@RequestMapping註解解析後的Method。

我們來看看,HandlerExecutionChain類的程式碼。

package org.springframework.web.servlet;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.util.CollectionUtils;

public class HandlerExecutionChain {

    private final Object handler;

    private HandlerInterceptor[] interceptors;

    private List<HandlerInterceptor> interceptorList;

    public HandlerExecutionChain(Object handler) {
        this(handler, null);
    }

    public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) {
        if (handler instanceof HandlerExecutionChain) {
            HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
            this.handler = originalChain.getHandler();
            this.interceptorList = new ArrayList<HandlerInterceptor>();
            CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
            CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
        }
        else {
            this.handler = handler;
            this.interceptors = interceptors;
        }
    }

    public Object getHandler() {
        return this.handler;
    }

    public void addInterceptor(HandlerInterceptor interceptor) {
        initInterceptorList();
        this.interceptorList.add(interceptor);
    }

    public void addInterceptors(HandlerInterceptor[] interceptors) {
        if (interceptors != null) {
            initInterceptorList();
            this.interceptorList.addAll(Arrays.asList(interceptors));
        }
    }

    private void initInterceptorList() {
        if (this.interceptorList == null) {
            this.interceptorList = new ArrayList<HandlerInterceptor>();
        }
        if (this.interceptors != null) {
            this.interceptorList.addAll(Arrays.asList(this.interceptors));
            this.interceptors = null;
        }
    }

    public HandlerInterceptor[] getInterceptors() {
        if (this.interceptors == null && this.interceptorList != null) {
            this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
        }
        return this.interceptors;
    }

}

一個攔截器列表,一個執行物件,這個類的內容十分的簡單,它蘊含的設計思想,卻十分的豐富。

1.攔截器組成的列表,在執行物件被呼叫的前後,會依次執行。這裡可以看成是一個的AOP環繞通知,攔截器可以對處理物件隨心所欲的進行處理和增強。這裡明顯是吸收了Struts2中攔截器的設計思想。這種AOP環繞式的擴充套件點設計,也幾乎成為所有框架必備的內容。

2.實際的處理物件,即handler物件,是由Object物件來引用的。

private final Object handler;

當我們拿到HandlerExecutionChain,就完成了request到Controller的路由操作。

介面卡匹配

有了Handler後,需要合適的HandlerAdapter對其進行操作,因而就要根據Handler進行匹配。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    for (HandlerAdapter ha : this.handlerAdapters) {
        if (logger.isTraceEnabled()) {
            logger.trace("Testing handler adapter [" + ha + "]");
        }
        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");
}

HandlerAdapter介面中定義了supports方法,用於檢測是否支援Handler。生產環境使用的RequestMappingHandlerAdapter在其基類AbstractHandlerMethodAdapter中實現了supports方法。

public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

supportsInternal方法在RequestMappingHandlerAdapter的實現裡預設返回true。因而RequestMappingHandlerAdapter就是用來支援型別為HandlerMethod的Handler的處理的。

攔截器處理

在SpringMVC中的攔截器介面HandlerInterceptor中定義了三個方法

public interface HandlerInterceptor {

    // 在Handler找到後,執行前攔截
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;

    // 在Handler執行後,檢視渲染前攔截
    void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;

    // 請求處理完成,檢視渲染後執行資源清理等
    void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;

}

可以很清晰地對應到doDispatcher方法中。需要注意的有幾點

  1. 前置處理preHandle,返回值為boolean。如果返回true,則執行下一個,如果返回false,則認為當前攔截器完成了請求,DispatcherServlet會直接返回,在返回前會呼叫所有攔截器的afterCompletion方法,完成清理工作。
  2. afterCompletion方法在遇到任何情況時都需要被執行,無論是成功返回還是丟擲異常。

執行請求

HandlerAdapter的handle方法完成請求的真正執行。在AbstractHandlerMethodAdapter中由handleInternal執行。

protected ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    checkRequest(request);


    // 執行HandlerMethod
    mav = invokeHandlerMethod(request, response, handlerMethod);

    // 處理快取
    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        }
        else {
            prepareResponse(response);
        }
    }

    return mav;
}

在invokeHandlerMethod中,HandlerMethod被封裝ServletInvocableHandlerMethod,包裹上方法執行需要的資訊。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        // 封裝HandlerMethod
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        // 非同步請求處理
        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.setTaskExecutor(this.taskExecutor);
        asyncManager.setAsyncWebRequest(asyncWebRequest);
        asyncManager.registerCallableInterceptors(this.callableInterceptors);
        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

        if (asyncManager.hasConcurrentResult()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            if (logger.isDebugEnabled()) {
                logger.debug("Found concurrent result value [" + result + "]");
            }
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }

        // 執行處理
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        // 封裝資料和檢視
        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        webRequest.requestCompleted();
    }
}

再到ServletInvocableHandlerMethod的invokeAndHandle方法

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 執行request
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    setResponseStatus(webRequest);

    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            mavContainer.setRequestHandled(true);
            return;
        }
    }
    else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    mavContainer.setRequestHandled(false);
    try {
        // 對返回值進行處理
        this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {
        if (logger.isTraceEnabled()) {
            logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
        }
        throw ex;
    }
}

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));
    }
    
    Object returnValue = doInvoke(args);
    if (logger.isTraceEnabled()) {
        logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
                "] returned [" + returnValue + "]");
    }
    return returnValue;
}

protected Object doInvoke(Object... args) throws Exception {
    ReflectionUtils.makeAccessible(getBridgedMethod());
    return getBridgedMethod().invoke(getBean(), args);
}

需要說明的一點是方法執行完成的返回值通過返回值處理器HandlerMethodReturnValueHandler進行處理。在RequestMappingHandlerAdapter的初始化中,內建了眾多的HandlerMethodReturnValueHandler來處理多種型別的返回值。

在完成請求執行後,doDispatcher方法中做了一個預設View的設定。

applyDefaultViewName(processedRequest, mv);

private void applyDefaultViewName(HttpServletRequest request, ModelAndView mv) throws Exception {
    if (mv != null && !mv.hasView()) {
        mv.setViewName(getDefaultViewName(request));
    }
}

而這個getDefaultViewName是通過RequestToViewNameTranslator的實現類來解析的

protected String getDefaultViewName(HttpServletRequest request) throws Exception {
    return this.viewNameTranslator.getViewName(request);
}

預設實現DefaultRequestToViewNameTranslator,根據配置的一些通用url進行匹配

public String getViewName(HttpServletRequest request) {
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    return (this.prefix + transformPath(lookupPath) + this.suffix);
}

檢視渲染

當請求完成後,返回的ModelAndView需要渲染到瀏覽器進行顯示。doDispatcher方法中processDispatchResult用來處理檢視。

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

    boolean errorView = false;

    // 異常處理
    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
        // 渲染執行
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
        if (logger.isDebugEnabled()) {
            logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                    "': assuming HandlerAdapter completed request handling");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }

    // 完成後執行攔截器的afterCompletion
    if (mappedHandler != null) {
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

render方法執行渲染,最終由View實現類執行

view.render(mv.getModelInternal(), request, response);

抽象類AbstractView執行對資料進行組裝,輸出操作交由子類完成

public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (logger.isTraceEnabled()) {
        logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
            " and static attributes " + this.staticAttributes);
    }

    // 組裝資料
    Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
    prepareResponse(request, response);
    // 渲染輸出
    renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

以通用的InternalResourceView舉例

protected void renderMergedOutputModel(
        Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

    // Expose the model object as request attributes.
    exposeModelAsRequestAttributes(model, request);

    // Expose helpers as request attributes, if any.
    exposeHelpers(request);

    // Determine the path for the request dispatcher.
    String dispatcherPath = prepareForRendering(request, response);

    // Obtain a RequestDispatcher for the target resource (typically a JSP).
    RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
    if (rd == null) {
        throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
                "]: Check that the corresponding file exists within your web application archive!");
    }

    // If already included or response already committed, perform include, else forward.
    if (useInclude(request, response)) {
        response.setContentType(getContentType());
        if (logger.isDebugEnabled()) {
            logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
        }
        rd.include(request, response);
    }

    else {
        // Note: The forwarded resource is supposed to determine the content type itself.
        if (logger.isDebugEnabled()) {
            logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
        }
        rd.forward(request, response);
    }
}

最終由java Servlet的RequestDispatcher完成輸出。其實就是做了一個跳轉

本章中以請求的正向主流程解析了DispatcherServlet及相關類完成此過程的原始碼,其主要過程則是HandlerExecutionChain,HandlerMapping,HandlerAdapter,View等元件的互動過程,貼兩張網上的核心原理圖,希望對大家理解SpringMVC的原理有幫助。

 

相關文章