SpringMVC之處理流程


 之前在學servlet時寫過JavaWeb與Asp.net工作原理比較分析,那篇主要是大致描述了下servlet的工作流程,今天在家了解了下springmvc的工作原理,與asp.net中的mvc進行了一下比較asp.net MVC 的處理流程,思想都是差不多,都是通過一個url怎么映射到類中做完處理返回瀏覽器的過程,首先要解決三大問題,一是url映射轉換成request和response對象的問題二是瀏覽器與服務端的數據交互問題三是服務端的request、response怎么響應給客戶端。今天了解了下它的運行流暢,其實網上也有好多教程。

一圖頂千言萬語,用數據、用圖說話,下圖是springmvc的工作原理圖。

SpringMVC工作流程

一、  用戶發送請求至前端控制器DispatcherServlet。 

     1.DispatcherServlet它也是servlet,load-on-startup=1,tomcat啟動時它也會初始化,初始化參數是contextConfigLocation上下文配置文件位置,參數值就是JavaWeb之Eclipse中使用Maven構建SpringMVC項目  配置的spring-mvc。在spring-mvc中可以配置自動掃描包名、默認注解映射支持、視圖解釋類、攔截器、對靜態資源文件的訪問等信息,通過自動掃描包名、注解映射支持、靜態資源訪問這些配置的信息在就會實例化HandlerMapping對象。這些對象是在tomcat進行參數初始化的時候也會實例化完成。在DispatcherServlet中維護着一個表,類似C#MVC中的RouteTable 路由表,存放的是HandlerMapping對象list.下面截圖是DispatcherServlet中的部分代碼,在DispatcherServlet中維護着handerMappings、handerAdapters等對象列表。在initStrategies中對上面的一些屬性進行初始化。

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
View Code

  2.客戶端發出請求,由 Tomcat 接收到這個請求,如果匹配 DispatcherServlet 在 web.xml 中配置的映射路徑,Tomcat 就將請求轉交給 DispatcherServlet 處理

二、  DispatcherServlet收到請求調用HandlerMapping處理器映射器。

  請求到達DispatcherServlet中之后,就是get、post這些請求,這些請求是DispatcherServlet的父類FrameworkServlet中定義着的,而在這些方法中又調用了processRequest,processRequest中調用了doService,DispatcherServlet重寫了doService方法,doService中主要設置了一些屬性和調用doDispatch方法,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 {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                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;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the 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);
                }
            }
        }
    }
View Code

上面的代碼是核心代碼,其實下面的幾項其實就是代碼的說明. 

processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

上面這兩句是判斷請求是不是上傳文件的請求

三、  處理器映射器找到具體的處理器(可以根據xml配置、注解進行查找),生成處理器對象及處理器攔截器(如果有則生成)一並返回給DispatcherServlet。

mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
    noHandlerFound(processedRequest, response);
    return;
}

getHander返回的是一個HandlerExecutionChain,這個HandlerExecutionChain中包含一個handler和多個HandlerInterceptor(攔截器),這個和asp.net中的管道模型有點類似,一個httphandler和多個httpmodule,httpmodule也是用來做攔截操作的。同時要留意HandlerExecutionChain是通過HandlerMapping對象的getHandler獲取的.其實這里還可以擴展比如攔截器的使用、HandlerMapping的介紹等,由於篇幅有限,會在以后的博客中一個一個的介紹。

四、  DispatcherServlet調用HandlerAdapter處理器適配器。

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            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");
    }
View Code

獲取適配器是通過遍歷handlerAdapters列表找的,HandlerAdapter包含了3個方法.boolean supports(Object handler);ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;long getLastModified(HttpServletRequest request, Object handler);

/*
 * Copyright 2002-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.lang.Nullable;

/**
 * MVC framework SPI, allowing parameterization of the core MVC workflow.
 *
 * <p>Interface that must be implemented for each handler type to handle a request.
 * This interface is used to allow the {@link DispatcherServlet} to be indefinitely
 * extensible. The {@code DispatcherServlet} accesses all installed handlers through
 * this interface, meaning that it does not contain code specific to any handler type.
 *
 * <p>Note that a handler can be of type {@code Object}. This is to enable
 * handlers from other frameworks to be integrated with this framework without
 * custom coding, as well as to allow for annotation-driven handler objects that
 * do not obey any specific Java interface.
 *
 * <p>This interface is not intended for application developers. It is available
 * to handlers who want to develop their own web workflow.
 *
 * <p>Note: {@code HandlerAdapter} implementors may implement the {@link
 * org.springframework.core.Ordered} interface to be able to specify a sorting
 * order (and thus a priority) for getting applied by the {@code DispatcherServlet}.
 * Non-Ordered instances get treated as lowest priority.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
 * @see org.springframework.web.servlet.handler.SimpleServletHandlerAdapter
 */
public interface HandlerAdapter {

    /**
     * Given a handler instance, return whether or not this {@code HandlerAdapter}
     * can support it. Typical HandlerAdapters will base the decision on the handler
     * type. HandlerAdapters will usually only support one handler type each.
     * <p>A typical implementation:
     * <p>{@code
     * return (handler instanceof MyHandler);
     * }
     * @param handler handler object to check
     * @return whether or not this object can use the given handler
     */
    boolean supports(Object handler);

    /**
     * Use the given handler to handle this request.
     * The workflow that is required may vary widely.
     * @param request current HTTP request
     * @param response current HTTP response
     * @param handler handler to use. This object must have previously been passed
     * to the {@code supports} method of this interface, which must have
     * returned {@code true}.
     * @throws Exception in case of errors
     * @return ModelAndView object with the name of the view and the required
     * model data, or {@code null} if the request has been handled directly
     */
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    /**
     * Same contract as for HttpServlet's {@code getLastModified} method.
     * Can simply return -1 if there's no support in the handler class.
     * @param request current HTTP request
     * @param handler handler to use
     * @return the lastModified value for the given handler
     * @see javax.servlet.http.HttpServlet#getLastModified
     * @see org.springframework.web.servlet.mvc.LastModified#getLastModified
     */
    long getLastModified(HttpServletRequest request, Object handler);

}
View Code

五、  HandlerAdapter經過適配調用具體的處理器(Controller,也叫后端控制器)。

六、  Controller執行完成返回ModelAndView。

七、  HandlerAdapter將controller執行結果ModelAndView返回給DispatcherServlet。

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

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

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

 上面的幾行代碼先判斷如果是get請求更新下lastModified請求頭,然后執行HandlerExecutionChain中的applyPreHandle這個方法.

    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;
    }
View Code
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable 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);
                }
            }
        }
    }
View Code

在applyPreHandle中它會遍歷該HandlerExecutionChain中所有的攔截器,然后使用攔截器通過preHandle對handler進行預處理,如果所有的攔截器都能處理那就會繼續往下執行,如果一旦有一個攔截器不能處理,就沒必要往下走了,那就會觸發triggerAfterCompletion方法,在triggerAfterCompletion中它是倒序遍歷的攔截器的,執行完triggerAfterCompletion返回false之后doDispatch這個方法就執行結束了,下面的八、九、十、十一就不再執行。從第五步括號里的備注也能猜出來HandlerExecutionChain.handler是什么,它可以是Controller。

八、  DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器。

 九、  ViewReslover解析后返回具體View。

applyDefaultViewName(processedRequest, mv);
    private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
        if (mv != null && !mv.hasView()) {
            String defaultViewName = getDefaultViewName(request);
            if (defaultViewName != null) {
                mv.setViewName(defaultViewName);
            }
        }
    }
View Code
    protected String getDefaultViewName(HttpServletRequest request) throws Exception {
        return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
    }
View Code

找到ModelAndView對應的viewname,設置成它的屬性值。

mappedHandler.applyPostHandle(processedRequest, response, mv);

applyPostHandle與applyPreHandle是對應着的,遍歷handler的攔截器,執行postHandle方法。

十、DispatcherServlet根據View進行渲染視圖(即將模型數據填充至視圖中)。

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
            @Nullable 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;
        }

        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }
View Code
    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 != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
        response.setLocale(locale);

        View view;
        String viewName = mv.getViewName();
        if (viewName != null) {
            // We need to resolve the view name.
            view = resolveViewName(viewName, 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 {
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            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;
        }
    }
View Code

在processDispatchResult方法中有一句render(mv, request, response),在render中先是獲取View對象然后調用view.render(mv.getModelInternal(), request, response);將view和model綁定來進行渲染試圖。

十一、 DispatcherServlet響應用戶。

響應用戶其實是在父類FrameworkServlet的processRequest方法中

十二、小結

上面黑字部分是參考其他博客的,紅色的是通過讀源碼找的,現在基本清楚springmvc大致的流程,其實如果繼續深入的話,還有好多知識點,這篇博客就寫到這,以后再慢慢補充。好久沒熬夜學習了,今天算是畢業之后最用功的一天了,哈哈...(今天四月一,愚人節!!!)

參考:https://www.cnblogs.com/xiaoxi/p/6164383.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM