SpringMVC DispatcherServlet執(zhí)行流程及源碼分析

你要知道的SpringMVC DispatcherServlet執(zhí)行流程及源碼分析都在這里

轉載請注明出處 http://www.itdecent.cn/p/0f981efdfbbd

本系列文章主要根據(jù)源碼講解SpringMVC的啟動過程,以及相關重要組件的源碼分析。閱讀此系列文章需要具備Spring以及SpringMVC相關知識。本文將分以下幾篇文章進行講解,讀者可按需查閱。

DispatcherServlet執(zhí)行流程及相關源碼分析

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

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

首先,讓我們站在Spring MVC的四大組件:DispatcherServlet、HandlerMapping、HandlerAdapter以及ViewResolver的角度來看一下Spring MVC對用戶請求的處理過程,有如下時序圖:

DispatcherServlet執(zhí)行序列圖

具體處理過程如下:

  • 1、用戶請求發(fā)送至DispatcherServlet類進行處理。
  • 2、DispatcherServlet類遍歷所有配置的HandlerMapping類請求查找Handler。
  • 3、HandlerMapping類根據(jù)request請求URL等信息查找能夠進行處理的Handler,以及相關攔截器interceptor并構造HandlerExecutionChain。
  • 4、HandlerMapping類將構造的HandlerExecutionChain類的對象返回給前端控制器DispatcherServlet類。
  • 5、前端控制器拿著上一步的Handler遍歷所有配置的HandlerAdapter類請求執(zhí)行Handler。
  • 6、HandlerAdapter類執(zhí)行相關Handler并獲取ModelAndView類的對象。
  • 7、HandlerAdapter類將上一步Handler執(zhí)行結果的ModelAndView 類的對象返回給前端控制器。
  • 8、DispatcherServlet類遍歷所有配置的ViewResolver類請求進行視圖解析。
  • 9、ViewResolver類進行視圖解析并獲取View對象。
  • 10、ViewResolver類向前端控制器返回上一步驟的View對象。
  • 11、DispatcherServlet類進行視圖View的渲染,填充Model。
  • 12、DispatcherServlet類向用戶返回響應。

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

DispatcherServlet類本質(zhì)上依舊是一個Servlet并且其父類實現(xiàn)了Servlet接口,我們知道,Servlet執(zhí)行Service()方法對用戶請求進行響應,根據(jù)前一篇文章的分析方法可以得到人如下的調(diào)用邏輯圖:

service方法調(diào)用邏輯

從上圖的源碼調(diào)用邏輯可以看出,HttpServlet抽象類實現(xiàn)了Servlet接口service(ServletRequest, ServletResponse)的方法,因此,用戶請求的第一執(zhí)行方法為該方法,該方法緊接著直接調(diào)用了service(HttpServletRequest, HttpServletResponse)方法,其子類FrameworkServlet抽象類重寫了該方法,因為多態(tài)的特性最終是調(diào)用了FrameworkServlet抽象類service(HttpServletRequest, HttpServletResponse)方法,FrameworkServlet抽象類同樣也重寫了doHead()、doPost()、doPut()、doDelete()doOptions()、doTrace()方法,service(ServletRequest, ServletResponse)方法根據(jù)請求類型的不同分別調(diào)用上述方法,上述六個方法都調(diào)用了processRequest()方法,而該方法最終調(diào)用了DispatcherServlet類doService()方法。通過層層分析,我們找到了最終要調(diào)用的處理用戶請求的方法,doService()之前的方法調(diào)用都比較簡單,這里不再逐一來查看源碼,有興趣的讀者可以自行查閱。

查看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()方法主要進行一些參數(shù)的設置,并將部分參數(shù)放入request請求中,真正執(zhí)行用戶請求并作出響應的方法則為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;
        //判斷是否解析了文件類型的數(shù)據(jù),如果有最終需要清理
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            //ModelAndView局部變量
            ModelAndView mv = null;
            //處理異常局部變量
            Exception dispatchException = null;

            try {
                //檢查是否包含文件等類型的數(shù)據(jù)
                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.
                //根據(jù)查找到的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;
                    }
                }
                
                /*
                按順序依次執(zhí)行HandlerInterceptor的preHandle方法
                如果任一HandlerInterceptor的preHandle方法沒有通過則不繼續(xù)進行處理
                */
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

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

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

                applyDefaultViewName(processedRequest, mv);
                //逆序執(zhí)行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) {
            //如果有異常按倒序執(zhí)行所有HandlerInterceptor的afterCompletion方法
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            //如果有異常按倒序執(zhí)行所有HandlerInterceptor的afterCompletion方法
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    //倒序執(zhí)行所有HandlerInterceptor的afterCompletion方法
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                //如果請求包含文件類型的數(shù)據(jù)則進行相關清理工作
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

根據(jù)上述源碼并結合文章開始講解的DispatcherServlet類結合三大組件對用戶請求的處理過程不難理解相關處理流程。

doDispatch()方法通過調(diào)用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()方法遍歷了開發(fā)者配置的所有HandlerMapping類根據(jù)request請求來查找HandlerExecutionChain,從這里可以看出,Spring MVC是支持用戶配置多個HandlerMapping類的,在處理用戶請求時會逐一查找,找到后立即返回,因此,如果多個HandlerMapping類都能夠處理同一request請求,只會返回第一個能夠處理的HandlerMapping類構造的HandlerExecutionChain,所以在配置HandlerMapping類時需要注意不要對同一請求多次進行處理,由于篇幅問題HandlerMapping類如何具體查找Handler并構造HandlerExecutionChain的細節(jié)不在此進行講解,如有興趣可以查閱本系列文章的第三篇SpringMVC HandlerMapping源碼分析。

如果沒有找到對應的HandlerExecutionChain對象,則會執(zhí)行noHandlerFound()方法,繼續(xù)查看其源碼如下:

    /**
     * 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,在開發(fā)的過程中,如果我們將具體的URL寫錯了則會遇到這個404錯誤。

繼續(xù)查看doDispatch()方法的源碼,如果找到了HandlerExecutionChain接下來會調(diào)用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()方法,該方法根據(jù)Handler是否實現(xiàn)某個特定的接口來判斷該HandlerAdapter是否能夠處理這個具體的Handler,這里使用適配器模式,通過這樣的方式就可以支持不同類型的HandlerAdapter。如果沒有查找到能夠處理HandlerHandlerAdapter則會拋出異常,如果在開發(fā)的過程中Handler在實現(xiàn)接口時出現(xiàn)了問題就可能會遇到上述異常。

查找到了對應的HandlerAdapter后就會調(diào)用HandlerExecutionChainapplyPreHandle()方法來執(zhí)行配置的所有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()方法會按照順序依次調(diào)用HandlerInterceptorpreHandle()方法,但當任一HandlerInterceptorpreHandle()方法返回了false就不再繼續(xù)執(zhí)行其他HandlerInterceptorpreHandle()方法,而是直接跳轉執(zhí)行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該變量加一,因此該方法會逆序執(zhí)行所有preHandle()方法返回了trueHandlerInterceptorafterCompletion()方法。到這里讀者已經(jīng)掌握了HandlerInterceptorpreHandle()方法以及afterCompletion()方法的執(zhí)行順序,這些內(nèi)容并不需要我們死記,需要知道其執(zhí)行順序查看源碼是最好的方法。

繼續(xù)閱讀doDispatch()方法的源碼,如果所有攔截器的preHandle()方法都返回了true沒有進行攔截,接下來前端控制器會請求執(zhí)行上文獲取的Handler,這個Handler就是開發(fā)的時候編寫的Controller,根據(jù)實現(xiàn)接口的不同執(zhí)行相關方法,并獲取到ModelAndView類的對象。

接下來會執(zhí)行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);
            }
        }
    }

可以發(fā)現(xiàn),postHandle()方法是按照逆序執(zhí)行。

執(zhí)行完postHandle()方法后,doDispatch()方法調(diào)用了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;
        }
        //執(zhí)行相關HandlerInterceptor的afterCompletion()方法
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

該方法傳入了一個異常類的對象dispatchException,閱讀doDispatch()方法的源碼可以看出,Spring MVC對整個doDispatch()方法用了嵌套的try-catch語句,內(nèi)層的try-catch用于捕獲HandlerMapping進行映射查找HandlerExecutionChain以及HandlerAdapter執(zhí)行具體Handler時的處理異常,并將異常傳入到上述processDispatchResult()方法中。

processDispatchResult()方法主要用于針對產(chǎn)生的異常來構造異常視圖,接著不管視圖是正常視圖還是異常視圖均調(diào)用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響應狀態(tài)字
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            //調(diào)用視圖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()方法通過調(diào)用resolveViewName()方法根據(jù)視圖名稱解析對應的視圖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類根據(jù)視圖名稱來解析對應的視圖View,如果找到則返回對應視圖View,沒有找到則返回null

回到前一個render()方法,如果上述方法返回的視圖為null則拋出異常,這個異常相信大多數(shù)人也見過,當開發(fā)時寫錯了返回的View視圖名稱時就會拋出該異常。接下來調(diào)用具體視圖的render()方法來進行Model數(shù)據(jù)的渲染填充,最終構造成完整的視圖。

到這里,doDispatch()的外層try-catch異常的作用我們就知道了,為了捕獲渲染視圖時的異常,通過兩層嵌套的try-catch,Spring MVC就能夠捕獲到三大組件在處理用戶請求時的異常,通過這樣的方法能夠很方便的實現(xiàn)統(tǒng)一的異常處理。

總結

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

備注

由于作者水平有限,難免出現(xiàn)紕漏,如有問題還請不吝賜教。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容