springMVC 執行流程

這裏寫圖片描述

分析:
dispatcherServlet配置在web.xml中,一般都是分發所有請求。發送一個請求進入到dispatcherServlet的doDispatcher函數中

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);

進入到getHandler,

/**
     * 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;
    }

可以看出mappedHandler 是一個HandlerExecutionChain。
什麼是HandlerExecutionChain?JavaDoc解釋:Handler execution chain, consisting of handler object and any handler interceptors.

這個HandlerExecutionChain是由HandlerMapping 獲取到的。

什麼是HandlerMapping ?解釋:Interface to be implemented by objects that define a mapping between requests and handler objects

此時的handlerMapping裏有什麼?
這裏寫圖片描述

getHandler()方法執行後 得到HandlerExecutionChain裏的handler是你請求的控制器。

注意:如果發送的請求沒有經過映射如a.html,mappedHandler 不爲null.因爲配置了

<mvc:default-servlet-handler />

靠的是handlerMapping裏的simpleUrlHandlerMapping(認爲請求的是靜態資源),得到HandlerExecutionChain裏的handler是DefaultServletHttpRequesthandler
如果沒有這個配置 則爲null.

接下來獲取HandlerAdapter

// Determine handler adapter for the current request.
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

解釋:Interface that must be implemented for each handler type to handle a request

然後執行

    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
        }
/**
     * 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;
    }

這個方法applyPreHandle()調用了攔截器的preHandler

接下來調用目標方法,執行HandlerAdapter對象的handle方法
HandlerAdapter作用:接過handlermapping解析請求得到的handler對象。在更精確的定位到能夠執行請求的方法。而且在調用目標方法過程中發生很多事情,如表單數據類型校驗,數據類型的轉換 ,格式化等

// Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

三個參數分別爲 請求(request current HTTP request),響應( response current HTTP response) ,映射的控制器(handler handler to use),返回一個ModelAndView

繼承HandlerAdapter接口的有
這裏寫圖片描述
我這裏進入到AbstractHandlerMethodAdapter。裏面有handleInternal方法。Use the given handler method to handle the request.

然後通過mappedHandler調用攔截器postHandle方法。

    mappedHandler.applyPostHandle(processedRequest, response, mv);
/**
     * 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);
            }
        }
    }

處理視圖:

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

進入processDispatchResult

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);
            }
        }

看到 判斷是否有異常,如果有,利用視圖解析器processHandlerException裏的HandlerExceptionResolver解析

之後渲染視圖(仍在processDispatchResult方法裏)


        // 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");
            }
        }
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 = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
            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 {
            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;
        }
    }

在這裏利用視圖解析器ViewResolver得到view對象,將邏輯視圖 變爲物理視圖。

最後調用攔截器的trigglerAfterCompletion(整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中我們可以在此記錄結束時間並輸出消耗時間,還可以進行一些資源清理,類似於try-catch-finally中的finally,但僅調用處理器執行鏈中preHandle返回true的攔截器的afterCompletion。)

if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }

以上是正常的流程。

如果發送一個請求,如a.html.如果在springMVC的配置文件裏沒有

<!-- 加載靜態資源 -->
<mvc:default-servlet-handler />

則404. 必須有這個配置 而且view裏有這個文件纔可以。

如果你配置了

<mvc:default-servlet-handler />

則必須配置

<!-- 配置返回信息編碼格式 -->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/plain; charset=UTF-8</value>
                        <value>text/json; charset=UTF-8</value>
                        <value>text/html; charset=UTF-8</value>
                        <value>application/json; charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

不然 controller無法進入。參考這個:mvc:annotation-driven以及@Controller和@RequestMapping的那些事

發佈了53 篇原創文章 · 獲贊 5 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章