SpringMVC整體工作流程

首先從入口開始:org.springframework.web.servlet.DispatcherServlet

public class DispatcherServlet extends FrameworkServlet {

    private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";

    /** Throw a NoHandlerFoundException if no Handler was found to process this request? *.*/
    private boolean throwExceptionIfNoHandlerFound = false;

    /** Perform cleanup of request attributes after include request?. */
    private boolean cleanupAfterInclude = true;

    /** MultipartResolver used by this servlet. */
    @Nullable
    private MultipartResolver multipartResolver;

    /** LocaleResolver used by this servlet. */
    @Nullable
    private LocaleResolver localeResolver;

    /** ThemeResolver used by this servlet. */
    @Nullable
    private ThemeResolver themeResolver;

    /** List of HandlerMappings used by this servlet. */
    @Nullable
    private List<HandlerMapping> handlerMappings;

    /** List of HandlerAdapters used by this servlet. */
    @Nullable
    private List<HandlerAdapter> handlerAdapters;

    /** List of HandlerExceptionResolvers used by this servlet. */
    @Nullable
    private List<HandlerExceptionResolver> handlerExceptionResolvers;

    /** RequestToViewNameTranslator used by this servlet. */
    @Nullable
    private RequestToViewNameTranslator viewNameTranslator;

    /** FlashMapManager used by this servlet. */
    @Nullable
    private FlashMapManager flashMapManager;

    /** List of ViewResolvers used by this servlet. */
    @Nullable
    private List<ViewResolver> viewResolvers;

    public DispatcherServlet() {
        super();
        setDispatchOptionsRequest(true);
    }

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    /**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {
        /*
        *從ApplicationContext中得到MultipartResolver.class類型
        *且名為multipartResolver的Bean伐坏,如:
        *1纫塌,CommonsMultipartResolver
        *2羽历,StandardServletMultipartResolver
        */
        initMultipartResolver(context);
        /*
        *從ApplicationContext中得到LocaleResolver.class類型
        *且名為localeResolver的Bean稽犁; 如果沒有得到,則使用
        *DispatcherServlet.properties文件中默認(rèn)配置的對(duì)象
        */
        initLocaleResolver(context);
        /*
        *從ApplicationContext中得到ThemeResolver.class類型
        *且名為themeResolver的Bean陨帆; 如果沒有得到曲秉,則使用
        *DispatcherServlet.properties文件中默認(rèn)配置的對(duì)象
        */
        initThemeResolver(context);
        /*
        *如果detectAllHandlerMappings屬性設(shè)置為ture(檢測(cè)所有handlerMapping),
        *則從ApplicationContext中得到所有HandlerMapping.class類的Bean疲牵; 
        *否則使用得到名為handlerMapping類型為HandlerMapping.class的Bean;
        *---------------------
        *例如以下常見的:
        *1,RequestMappingHandlerMapping
        *2,SimpleUrlHandlerMapping
        *3,ControllerEndpointHandlerMapping
        *4,WebMvcEndpointHandlerMapping
        *5,BeanNameUrlHandlerMapping
        *6,RouterFunctionMapping
        *7,PropertySourcedRequestMappingHandlerMapping
        *8,WelcomePageHandlerMapping
        *9,CompositeHandlerMapping
        */
        initHandlerMappings(context);
        /*
        *如果detectAllHandlerAdapters屬性設(shè)置為ture(檢測(cè)所有handlerAdapter)岸浑,
        *則從ApplicationContext中得到所有HandlerAdapter.class類的Bean; 
        *否則使用得到名為handlerAdapter類型為HandlerAdapter.class的Bean;
        *---------------------
        *例如以下常見的:
        *1,SimpleServletHandlerAdapter
        *2,SimpleControllerHandlerAdapter
        *3,HttpRequestHandlerAdapter
        *4,HandlerFunctionAdapter
        *5,RequestMappingHandlerAdapter
        *6,CompositeHandlerAdapter
        */
        initHandlerAdapters(context);
        /*
        *如果detectAllHandlerExceptionResolvers屬性設(shè)置為ture(檢測(cè)所有HandlerExceptionResolver)瑰步,
        *則從ApplicationContext中得到所有HandlerExceptionResolver.class類的Bean; 
        *否則使用得到名為handlerExceptionResolver類型為HandlerExceptionResolver.class的Bean;
        *---------------------
        *例如以下常見的:
        *1,SimpleMappingExceptionResolver
        *2,ResponseStatusExceptionResolver
        *3,DefaultHandlerExceptionResolver
        *4,ExceptionHandlerExceptionResolver
        *5,DefaultErrorAttributes
        *6,HandlerExceptionResolverComposite
        *7,CompositeHandlerExceptionResolver
        */
        initHandlerExceptionResolvers(context);
        /*
        *從ApplicationContext中得到RequestToViewNameTranslator.class類型
        *且名為viewNameTranslator的Bean璧眠; 如果沒有得到缩焦,則使用
        *DispatcherServlet.properties文件中默認(rèn)配置的對(duì)象
        */
        initRequestToViewNameTranslator(context);
        /*
        *如果detectAllViewResolvers屬性設(shè)置為ture(檢測(cè)所有l(wèi)ViewResolver),
        *則從ApplicationContext中得到所有ViewResolver.class類的Bean责静; 
        *否則使用得到名為viewResolver類型為ViewResolver.class的Bean;
        *---------------------
        *例如以下常見的:
        *1,InternalResourceViewResolver
        *2,ResourceBundleViewResolver
        *3,MustacheViewResolver
        *4,XsltViewResolver
        *5,TilesViewResolver
        *6,XmlViewResolver
        *7,BeanNameViewResolver
        *8,ContentNegotiatingViewResolver
        *9,GroovyMarkupViewResolver
        *10,FreeMarkerViewResolver
        *11,ViewResolverComposite
        */
        initViewResolvers(context);
        /*
        *從ApplicationContext中得到FlashMapManager.class類型
        *且名為flashMapManager的Bean袁滥; 如果沒有得到,則使用
        *DispatcherServlet.properties文件中默認(rèn)配置的對(duì)象
        */
        initFlashMapManager(context);
    }

    /**
     * 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 {
        logRequest(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<>();
            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.
        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());

        if (this.flashMapManager != null) {
            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);
                }
            }
        }
    }

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

小結(jié):對(duì)請(qǐng)求處理的大體過程

  1. 檢查是不是文件上傳(Multipart)灾螃,如果是則對(duì)Request進(jìn)行解析返回包裝后的HttpServletRequest

  2. 根據(jù)HttpServletRequest得到匹配的HandlerMapping题翻,并返回HandlerMapping的getHandler方法返回的結(jié)果HandlerExecutionChain

    如果返回的HandlerExecutionChain為null,當(dāng)throwExceptionIfNoHandlerFound屬性為ture(默認(rèn)為false)時(shí)則拋出NoHandlerFoundException異常腰鬼,否則直接響應(yīng)404

  3. 使用HandlerExecutionChain的getHandler()方法的返回結(jié)果作為參數(shù)來查找匹配的HandlerAdapter

    HandlerExecutionChain的getHandler()方法的返回結(jié)果(Handler)作為參數(shù)調(diào)用每個(gè)HandlerAdapter的supports方法嵌赠,如果找到則返回該HandlerAdapter,否則拋出ServletException異常

  4. 使用Request熄赡,Response以及Handler作為參數(shù)執(zhí)行HandlerAdapter的handle方法姜挺,并得到返回值ModelAndView

  5. 給ModelAndView設(shè)值默認(rèn)的viewName值(如果返回的ModelAndView沒有設(shè)置viewName)

    如果viewNameTranslator不為null,則以HttpServletRequest作為參數(shù)執(zhí)行其getViewName方法得到默認(rèn)的名稱(也可能為null)

  6. 捕獲上面過程中執(zhí)行得到的Exception(沒發(fā)生異常則為null),并結(jié)合HttpServletRequest彼硫、HttpServletResponse炊豪、HandlerExecutionChain、ModelAndView作為參數(shù)執(zhí)行processDispatchResult方法
    1)如果Exception不為空拧篮,且不是ModelAndViewDefiningException異常词渤,則執(zhí)行processHandlerException方法返回ModelAndView;

    迭代執(zhí)行每個(gè)HandlerExceptionResolver的resolveException方法串绩,如果中途返回的ModelAndView不為空則返回缺虐,如果最終得到的ModelAndView為null

    2)如果ModelAndView中的viewName不為空則遍歷所有ViewResolver并執(zhí)行resolveViewName方法的到View實(shí)例,如果最終得到的為null赏参,則拋出ServletException異常志笼; 如果viewName為空沿盅,則直接從ModelAndView對(duì)象中返回View實(shí)例,如果為空依然拋出異常纫溃。

    3)執(zhí)行View的render訪問完成視圖的渲染

  7. 清除Multipart

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末腰涧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子紊浩,更是在濱河造成了極大的恐慌窖铡,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坊谁,死亡現(xiàn)場(chǎng)離奇詭異费彼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)口芍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門箍铲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鬓椭,你說我怎么就攤上這事颠猴。” “怎么了小染?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵翘瓮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我裤翩,道長(zhǎng)资盅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任踊赠,我火速辦了婚禮呵扛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘筐带。我一直安慰自己择份,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布烫堤。 她就那樣靜靜地躺著荣赶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸽斟。 梳的紋絲不亂的頭發(fā)上拔创,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音富蓄,去河邊找鬼剩燥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灭红。 我是一名探鬼主播侣滩,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼变擒!你這毒婦竟也來了君珠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤娇斑,失蹤者是張志新(化名)和其女友劉穎策添,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毫缆,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唯竹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苦丁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浸颓。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖旺拉,靈堂內(nèi)的尸體忽然破棺而出猾愿,到底是詐尸還是另有隱情,我是刑警寧澤账阻,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站泽本,受9級(jí)特大地震影響淘太,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜规丽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一蒲牧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赌莺,春花似錦冰抢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至巢音,卻和暖如春遵倦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背官撼。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工梧躺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人傲绣。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓掠哥,卻偏偏與公主長(zhǎng)得像巩踏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子续搀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容