1. 程式人生 > >DispatcherServlet執行流程及相關原始碼分析

DispatcherServlet執行流程及相關原始碼分析

DispatcherServlet執行流程及相關原始碼分析

在前一篇文章SpringMVC 啟動流程及相關原始碼分析中,詳細探討了Spring MVCWeb容器中部署後的啟動過程,以及相關原始碼分析,同時也討論了DispatcherServlet類的初始化建立過程,相關內容在此不再贅述,如有需求可查閱。

本文主要講解DispatcherServlet類獲取使用者請求到響應的全過程,並針對相關原始碼進行分析。對於基本的MVC架構本文不再進行講解,有需要的讀者可自行查閱。

首先,讓我們站在Spring MVC的四大元件:DispatcherServletHandlerMapping

HandlerAdapter以及ViewResolver的角度來看一下Spring MVC對使用者請求的處理過程,有如下時序圖:

DispatcherServlet執行序列圖

具體處理過程如下:

  • 1、使用者請求傳送至DispatcherServlet類進行處理。
  • 2、DispatcherServlet類遍歷所有配置的HandlerMapping類請求查詢Handler
  • 3、HandlerMapping類根據request請求URL等資訊查詢能夠進行處理的Handler,以及相關攔截器interceptor並構造HandlerExecutionChain
  • 4、HandlerMapping類將構造的HandlerExecutionChain類的物件返回給前端控制器DispatcherServlet類
  • 5、前端控制器拿著上一步的Handler遍歷所有配置的HandlerAdapter類請求執行Handler
  • 6、HandlerAdapter類執行相關Handler並獲取ModelAndView類的物件。
  • 7、HandlerAdapter類將上一步Handler執行結果的ModelAndView 類的物件返回給前端控制器。
  • 8、DispatcherServlet類遍歷所有配置的ViewResolver類請求進行檢視解析。
  • 9、ViewResolver類進行檢視解析並獲取View物件。
  • 10、ViewResolver類向前端控制器返回上一步驟的View物件。
  • 11、DispatcherServlet類進行檢視View的渲染,填充Model
  • 12、DispatcherServlet類向用戶返回響應。

通過時序圖和上面的講解不難發現,整個Spring MVC對於使用者請求的響應和處理都是以DispatcherServlet類為核心,其他三大元件均與前端控制器進行互動,三大元件之間沒有互動並且互相解耦,因此,三大元件可以替換不同的實現而互相沒有任何影響,提高了整個架構的穩定性並且降低了耦合度。接下來會按照上述的響應過程逐一進行講解。

DispatcherServlet類本質上依舊是一個Servlet並且其父類實現了Servlet介面,我們知道,Servlet執行Service()方法對使用者請求進行響應,根據前一篇文章的分析方法可以得到人如下的呼叫邏輯圖:

service方法呼叫邏輯

從上圖的原始碼呼叫邏輯可以看出,HttpServlet抽象類實現了Servlet介面service(ServletRequest, ServletResponse)的方法,因此,使用者請求的第一執行方法為該方法,該方法緊接著直接呼叫了service(HttpServletRequest, HttpServletResponse)方法,其子類FrameworkServlet抽象類重寫了該方法,因為多型的特性最終是呼叫了FrameworkServlet抽象類service(HttpServletRequest, HttpServletResponse)方法,FrameworkServlet抽象類同樣也重寫了doHead()doPost()doPut()doDelete()doOptions()doTrace()方法,service(ServletRequest, ServletResponse)方法根據請求型別的不同分別呼叫上述方法,上述六個方法都呼叫了processRequest()方法,而該方法最終呼叫了DispatcherServlet類doService()方法。通過層層分析,我們找到了最終要呼叫的處理使用者請求的方法,doService()之前的方法呼叫都比較簡單,這裡不再逐一來檢視原始碼,有興趣的讀者可以自行查閱。

檢視doService()的原始碼如下:

    /**
     * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
     * for the actual dispatching.
     */
    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (logger.isDebugEnabled()) {
            String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
            logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
                    " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
        }

        // 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)) {
            attributesSnapshot = new HashMap<String, Object>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }

        // Make framework objects available to handlers and view objects.
        /*
        將當前Servlet的子IoC容器放入request請求中
        由此,我們可以訪問到當前IoC子容器以及根IoC容器中的Bean
        */
        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 {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Restore the original attribute snapshot, in case of an include.
                if (attributesSnapshot != null) {
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }

doService()方法主要進行一些引數的設定,並將部分引數放入request請求中,真正執行使用者請求並作出響應的方法則為doDispatch()方法,檢視doDispatch()方法的原始碼如下:

    /**
     * Process the actual dispatching to the handler.
     * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
     * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
     * to find the first that supports the handler class.
     * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
     * themselves to decide which methods are acceptable.
     * @param request current HTTP request
     * @param response current HTTP response
     * @throws Exception in case of any kind of processing failure
     */
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //使用者的request請求
        HttpServletRequest processedRequest = request;
        //HandlerExecutionChain區域性變數
        HandlerExecutionChain mappedHandler = null;
        //判斷是否解析了檔案型別的資料,如果有最終需要清理
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            //ModelAndView區域性變數
            ModelAndView mv = null;
            //處理異常區域性變數
            Exception dispatchException = null;

            try {
                //檢查是否包含檔案等型別的資料
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                //向HandlerMapping請求查詢HandlerExecutionChain
                mappedHandler = getHandler(processedRequest);
                //如果HandlerExecutionChain為null,則沒有能夠進行處理的Handler,丟擲異常
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                //根據查詢到的Handler請求查詢能夠進行處理的HandlerAdapter
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                //判斷自上次請求後是否有修改,沒有修改直接返回響應
                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;
                    }
                }
                
                /*
                按順序依次執行HandlerInterceptor的preHandle方法
                如果任一HandlerInterceptor的preHandle方法沒有通過則不繼續進行處理
                */
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                //通過HandlerAdapter執行查詢到的handler
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

                applyDefaultViewName(processedRequest, mv);
                //逆序執行HandlerInterceptor的postHandle方法
                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);
            }
            //渲染檢視填充Model,如果有異常渲染異常頁面
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            //如果有異常按倒序執行所有HandlerInterceptor的afterCompletion方法
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            //如果有異常按倒序執行所有HandlerInterceptor的afterCompletion方法
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    //倒序執行所有HandlerInterceptor的afterCompletion方法
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                //如果請求包含檔案型別的資料則進行相關清理工作
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

根據上述原始碼並結合文章開始講解的DispatcherServlet類結合三大元件對使用者請求的處理過程不難理解相關處理流程。

doDispatch()方法通過呼叫getHandler()方法並傳入reuqest通過HandlerMapping查詢HandlerExecutionChain,檢視其原始碼如下:

    /**
     * Return the HandlerExecutionChain for this request.
     * <p>Tries all handler mappings in order.
     * @param request current HTTP request
     * @return the HandlerExecutionChain, or {@code null} if no handler could be found
     */
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : this.handlerMappings) {
            if (logger.isTraceEnabled()) {
                logger.trace(
                        "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
    }

getHandler()方法遍歷了開發者配置的所有HandlerMapping類根據request請求來查詢HandlerExecutionChain,從這裡可以看出,Spring MVC是支援使用者配置多個HandlerMapping類的,在處理使用者請求時會逐一查詢,找到後立即返回,因此,如果多個HandlerMapping類都能夠處理同一request請求,只會返回第一個能夠處理的HandlerMapping類構造的HandlerExecutionChain,所以在配置HandlerMapping類時需要注意不要對同一請求多次進行處理,由於篇幅問題HandlerMapping類如何具體查詢Handler並構造HandlerExecutionChain的細節不在此進行講解,如有興趣可以查閱本系列文章的第三篇SpringMVC HandlerMapping原始碼分析

如果沒有找到對應的HandlerExecutionChain物件,則會執行noHandlerFound()方法,繼續檢視其原始碼如下:

    /**
     * No handler found -> set appropriate HTTP response status.
     * @param request current HTTP request
     * @param response current HTTP response
     * @throws Exception if preparing the response failed
     */
    protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (pageNotFoundLogger.isWarnEnabled()) {
            pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) +
                    "] in DispatcherServlet with name '" + getServletName() + "'");
        }
        if (this.throwExceptionIfNoHandlerFound) {
            throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
                    new ServletServerHttpRequest(request).getHeaders());
        }
        else {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        }
    }

如果沒有找到對應的HandlerExecutionChain則會丟擲異常NoHandlerFoundException,在開發的過程中,如果我們將具體的URL寫錯了則會遇到這個404錯誤。

繼續檢視doDispatch()方法的原始碼,如果找到了HandlerExecutionChain接下來會呼叫getHandlerAdapter()方法來查詢能夠對Handler進行處理的HandlerAdapter,檢視其原始碼如下:

    /**
     * Return the HandlerAdapter for this handler object.
     * @param handler the handler object to find an adapter for
     * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
     */
    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");
    }

HandlerMapping類似,查詢能夠處理具體HandlerHandlerAdapter時同樣會遍歷所有配置了的HandlerAdapterHandlerAdapter是一個介面包含一個support()方法,該方法根據Handler是否實現某個特定的介面來判斷該HandlerAdapter是否能夠處理這個具體的Handler,這裡使用介面卡模式,通過這樣的方式就可以支援不同型別的HandlerAdapter。如果沒有查詢到能夠處理HandlerHandlerAdapter則會丟擲異常,如果在開發的過程中Handler在實現介面時出現了問題就可能會遇到上述異常。

查詢到了對應的HandlerAdapter後就會呼叫HandlerExecutionChainapplyPreHandle()方法來執行配置的所有HandlerInteceptorpreHandle()方法,檢視其原始碼如下:

    /**
     * Apply preHandle methods of registered interceptors.
     * @return {@code true} if the execution chain should proceed with the
     * next interceptor or the handler itself. Else, DispatcherServlet assumes
     * that this interceptor has already dealt with the response itself.
     */
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }

HandlerExecutionChainapplyPreHandle()方法會按照順序依次呼叫HandlerInterceptorpreHandle()方法,但當任一HandlerInterceptorpreHandle()方法返回了false就不再繼續執行其他HandlerInterceptorpreHandle()方法,而是直接跳轉執行triggerAfterCompletion()方法,檢視該方法原始碼如下:

    /**
     * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
     * Will just invoke afterCompletion for all interceptors whose preHandle invocation
     * has successfully completed and returned true.
     */
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
            throws Exception {

        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = this.interceptorIndex; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                try {
                    interceptor.afterCompletion(request, response, this.handler, ex);
                }
                catch (Throwable ex2) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                }
            }
        }
    }

這裡遍歷的下標為interceptorIndex,該變數在前一個方法applyPreHandle()方法中賦值,如果preHandle()方法返回true該變數加一,因此該方法會逆序執行所有preHandle()方法返回了trueHandlerInterceptorafterCompletion()方法。到這裡讀者已經掌握了HandlerInterceptorpreHandle()方法以及afterCompletion()方法的執行順序,這些內容並不需要我們死記,需要知道其執行順序檢視原始碼是最好的方法。

繼續閱讀doDispatch()方法的原始碼,如果所有攔截器的preHandle()方法都返回了true沒有進行攔截,接下來前端控制器會請求執行上文獲取的Handler,這個Handler就是開發的時候編寫的Controller,根據實現介面的不同執行相關方法,並獲取到ModelAndView類的物件。

接下來會執行HandlerInterceptorpostHandle()方法,具體原始碼如下:

    /**
     * Apply postHandle methods of registered interceptors.
     */
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = interceptors.length - 1; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }
    }

可以發現,postHandle()方法是按照逆序執行。

執行完postHandle()方法後,doDispatch()方法呼叫了processDispatchResult()方法,其原始碼如下:

    /**
     * Handle the result of handler selection and handler invocation, which is
     * either a ModelAndView or an Exception to be resolved to a ModelAndView.
     */
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

        boolean errorView = false;
        //判斷HandlerMapping、HandlerAdapter處理時的異常是否為空
        if (exception != null) {
            //上述兩個元件處理時的異常不為空
            //如果為ModelAndViewDefiningException異常,則獲取一個異常檢視
            if (exception instanceof ModelAndViewDefiningException) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            }
            //如果不為ModelAndViewDefiningException異常,進行異常檢視的獲取
            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?
        //判斷mv是否為空,不管是正常的ModelAndView還是異常的ModelAndView,只要存在mv就進行檢視渲染
        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;
        }
        //執行相關HandlerInterceptor的afterCompletion()方法
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

該方法傳入了一個異常類的物件dispatchException,閱讀doDispatch()方法的原始碼可以看出,Spring MVC對整個doDispatch()方法用了巢狀的try-catch語句,內層的try-catch用於捕獲HandlerMapping進行對映查詢HandlerExecutionChain以及HandlerAdapter執行具體Handler時的處理異常,並將異常傳入到上述processDispatchResult()方法中。

processDispatchResult()方法主要用於針對產生的異常來構造異常檢視,接著不管檢視是正常檢視還是異常檢視均呼叫render()方法來渲染,檢視render()方法的具體原始碼如下:

    /**
     * Render the given ModelAndView.
     * <p>This is the last stage in handling a request. It may involve resolving the view by name.
     * @param mv the ModelAndView to render
     * @param request current HTTP servlet request
     * @param response current HTTP servlet response
     * @throws ServletException if view is missing or cannot be resolved
     * @throws Exception if there's a problem rendering the view
     */
    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Determine locale for request and apply it to the response.
        Locale locale = this.localeResolver.resolveLocale(request);
        response.setLocale(locale);

        View view;
        if (mv.isReference()) {
            // We need to resolve the view name.
            // 解析檢視名稱獲取對應檢視View
            view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
            //如果檢視View為空丟擲異常
            if (view == null) {
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                        "' in servlet with name '" + getServletName() + "'");
            }
        }
        else {
            // No need to lookup: the ModelAndView object contains the actual View object.
            view = mv.getView();
            if (view == null) {
                throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                        "View object in servlet with name '" + getServletName() + "'");
            }
        }

        // Delegate to the View object for rendering.
        if (logger.isDebugEnabled()) {
            logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
        }
        try {
            //設定Http響應狀態字
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            //呼叫檢視View的render方法通過Model來渲染檢視
            view.render(mv.getModelInternal(), request, response);
        }
        catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
                        getServletName() + "'", ex);
            }
            throw ex;
        }
    }

render()方法通過呼叫resolveViewName()方法根據檢視名稱解析對應的檢視View,該方法原始碼如下:

    /**
     * Resolve the given view name into a View object (to be rendered).
     * <p>The default implementations asks all ViewResolvers of this dispatcher.
     * Can be overridden for custom resolution strategies, potentially based on
     * specific model attributes or request parameters.
     * @param viewName the name of the view to resolve
     * @param model the model to be passed to the view
     * @param locale the current locale
     * @param request current HTTP servlet request
     * @return the View object, or {@code null} if none found
     * @throws Exception if the view cannot be resolved
     * (typically in case of problems creating an actual View object)
     * @see ViewResolver#resolveViewName
     */
    protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
            HttpServletRequest request) throws Exception {

        for (ViewResolver viewResolver : this.viewResolvers) {
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                return view;
            }
        }
        return null;
    }

resolveViewName()方法通過遍歷配置的所有ViewResolver類根據檢視名稱來解析對應的檢視View,如果找到則返回對應檢視View,沒有找到則返回null

回到前一個render()方法,如果上述方法返回的檢視為null則丟擲異常,這個異常相信大多數人也見過,當開發時寫錯了返回的View檢視名稱時就會丟擲該異常。接下來呼叫具體檢視的render()方法來進行Model資料的渲染填充,最終構造成完整的檢視。

到這裡,doDispatch()的外層try-catch異常的作用我們就知道了,為了捕獲渲染檢視時的異常,通過兩層巢狀的try-catchSpring MVC就能夠捕獲到三大元件在處理使用者請求時的異常,通過這樣的方法能夠很方便的實現統一的異常處理。

總結

通過前文的原始碼分析,我們能夠清楚的認識到Spring MVC對使用者請求的處理過程,進一步加深對Spring MVC的理解。