(3)Spring MVC請(qǐng)求分發(fā)部分源碼分析

第一部分

  • Spring MVC所有的開(kāi)始都會(huì)集中在Servlet上面许饿,查看Spring MVC的源碼窖贤,你會(huì)發(fā)現(xiàn)Servlet中的所有處理請(qǐng)求的方法(doGet拜效、doPost...)鲫构,最后都會(huì)委托給org.springframework.web.servlet.DispatcherServlet中的doDispatch(HttpServletRequest request, HttpServletResponse response)方法,而這個(gè)方法包含了一個(gè)請(qǐng)求從被分發(fā)奋救,處理岭参,通過(guò)HTTP返回瀏覽器數(shù)據(jù)(response.XXX)的所有邏輯,也就是說(shuō)一個(gè)request的一生都會(huì)在這里度過(guò)尝艘。

  • 對(duì)于演侯,核心代碼,下面的源碼中已經(jīng)打上注釋背亥∶爰剩可以先看第二部分,再看下面的源碼狡汉,這里不再贅述娄徊。

  • spring mvc的攔截器嚴(yán)格上說(shuō)不是責(zé)任鏈模式,但是就是責(zé)任鏈的思想轴猎,但比其更復(fù)雜嵌莉。HandlerInterceptor帶給我們的是一種具有高靈活性的細(xì)粒度的請(qǐng)求處理流程的攔截方案进萄。

    /**
     * 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 {
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

                // ★根據(jù)請(qǐng)求(HttpServletRequest)找到對(duì)應(yīng)的HandlerExecutionChain
                //(包含該request對(duì)應(yīng)的Handler和一系列攔截器HandlerInterceptor)
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                //★找到Handler對(duì)應(yīng)的適配器捻脖,方便DispatcherServlet了(正確調(diào)用Handler中的處理方法)
                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;
                    }
                }
                //★依次調(diào)用該request的所有攔截器的preHandler方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                //★真正開(kāi)始調(diào)用Handler中的處理方法,即請(qǐng)求被分發(fā)到Handler中處理
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

                //★依次調(diào)用該request的所有攔截器的prePost方法
                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);
            }
             //Handler的handle(...)執(zhí)行后中鼠,返回ModelAndView后可婶,這里主要的工作:
            //根據(jù)ModelAndView找到view,渲染援雇,交給response返回
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
    }

第二部分

  • 1矛渴、首先看org.springframework.web.servlet.DispatcherServlet,里面有一個(gè)重要的成員變量handlerMappings。
    /** List of HandlerMappings used by this servlet */
    private List<HandlerMapping> handlerMappings;

看到熟悉的HandlerMapping吧惫搏,我們知道這個(gè)接口具温,主要工作是幫助DispatcherServlet進(jìn)行Web請(qǐng)求的URL到具體處理類的匹配。

/**
 * Interface to be implemented by objects that define a mapping between
 * requests and handler objects.
**/
public interface HandlerMapping {
    /**
     * Return a handler and any interceptors for this request. The choice may be made
     * on request URL, session state, or any factor the implementing class chooses.
     * <p>The returned HandlerExecutionChain contains a handler Object, rather than
     * even a tag interface, so that handlers are not constrained in any way.
     * For example, a HandlerAdapter could be written to allow another framework's
     * handler objects to be used.
     * <p>Returns {@code null} if no match was found. This is not an error.
     * The DispatcherServlet will query all registered HandlerMapping beans to find
     * a match, and only decide there is an error if none can find a handler.
     * @param request current HTTP request
     * @return a HandlerExecutionChain instance containing handler object and
     * any interceptors, or {@code null} if no mapping found
     * @throws Exception if there is an internal error
     */
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
  • 2筐赔、現(xiàn)在看看org.springframework.web.servlet.DispatcherServlet铣猩,根據(jù)請(qǐng)求找到具體的Handler(我們用的最多的Handler就是Controller)的getHandler(...)方法吧!
  • 我們?cè)谶@個(gè)方法里面可以發(fā)現(xiàn),會(huì)遍歷所有的HandlerMapping茴丰,將為request找Handler委托給HandlerMapping达皿,說(shuō)白了天吓,就是一個(gè)個(gè)試。如果找到就會(huì)返回一個(gè)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;
    }
  • 3峦椰、我們知道在Spring MVC中龄寞,任何可以用于Web請(qǐng)求處理的處理對(duì)象統(tǒng)稱為Handler(Controller是Handler的一種特殊類型)。也就是可能會(huì)有很多類型的Handler那么org.springframework.web.servlet.DispatcherServlet是如何判斷我們使用的是哪一種類型汤功?又如何決定調(diào)用哪一個(gè)方法處理請(qǐng)求呢物邑?
  • 我們會(huì)在org.springframework.web.servlet.DispatcherServlet發(fā)現(xiàn)這樣一個(gè)成員變量。
    /** List of HandlerAdapters used by this servlet */
    private List<HandlerAdapter> handlerAdapters;
  • 這時(shí)冤竹,我們的HandlerAdapter就要登場(chǎng)了拂封!它就是為了解決不同類型的Handler對(duì)DispatcherServlert造成的困擾而生的。

  • 每種Handler都應(yīng)該有對(duì)應(yīng)的HandlerAdapter鹦蠕,然后DispatcherHandler只要根據(jù)Handler冒签,找到對(duì)應(yīng)的HandlerAdapter,然后調(diào)用HandlerAdapter中的ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)即可正確調(diào)用Handler中的處理方法钟病。這部分邏輯在DispatcherServlet中的doDispatch(...)方法中萧恕。

// Determine handler adapter for the current request.
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...
// Actually invoke the handler.
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
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
     */
    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);

}

最后看看org.springframework.web.servlet.DispatcherServlet中的getHandlerAdapter就會(huì)一目了然。

    /**
     * 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 + "]");
            }
            //判斷該HandlerAdapter是否支持該Handler
            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");
    }

  • 4.現(xiàn)在org.springframework.web.servlet.DispatcherServlet終于可以聰明的為每個(gè)request肠阱,找到對(duì)應(yīng)的Handler了票唆。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市屹徘,隨后出現(xiàn)的幾起案子走趋,更是在濱河造成了極大的恐慌,老刑警劉巖噪伊,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件簿煌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡鉴吹,警方通過(guò)查閱死者的電腦和手機(jī)姨伟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)豆励,“玉大人夺荒,你說(shuō)我怎么就攤上這事×颊簦” “怎么了技扼?”我有些...
    開(kāi)封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)嫩痰。 經(jīng)常有香客問(wèn)我剿吻,道長(zhǎng),這世上最難降的妖魔是什么始赎? 我笑而不...
    開(kāi)封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任和橙,我火速辦了婚禮仔燕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘魔招。我一直安慰自己晰搀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布办斑。 她就那樣靜靜地躺著外恕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乡翅。 梳的紋絲不亂的頭發(fā)上鳞疲,一...
    開(kāi)封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音蠕蚜,去河邊找鬼尚洽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛靶累,可吹牛的內(nèi)容都是我干的腺毫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼挣柬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼潮酒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起邪蛔,我...
    開(kāi)封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤急黎,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后侧到,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體勃教,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年床牧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荣回。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遭贸。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡戈咳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出壕吹,到底是詐尸還是另有隱情著蛙,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布耳贬,位于F島的核電站踏堡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏咒劲。R本人自食惡果不足惜顷蟆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一诫隅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧帐偎,春花似錦逐纬、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至漫贞,卻和暖如春甸箱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背迅脐。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工芍殖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谴蔑。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓围小,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親树碱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肯适,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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

  • Spring的模型-視圖-控制器(MVC)框架是圍繞一個(gè)DispatcherServlet來(lái)設(shè)計(jì)的,這個(gè)Servl...
    4ea0af17fd67閱讀 1,052評(píng)論 0 5
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理成榜,服務(wù)發(fā)現(xiàn)框舔,斷路器,智...
    卡卡羅2017閱讀 134,651評(píng)論 18 139
  • Spring Web框架架構(gòu)的主要部分是DispatcherServlet赎婚。也就是本文中重點(diǎn)介紹的對(duì)象刘绣。 在本文的...
    極樂(lè)君閱讀 1,417評(píng)論 0 11
  • 從網(wǎng)絡(luò)下載圖片并保存到磁盤文件中 建立保存圖片的目錄如果目錄不存在,則建立文件目錄挣输。使用os.makedirs(f...
    4ffde5305e8f閱讀 47,914評(píng)論 2 9
  • 滿庭枯樹(shù)生衰草纬凤,舊事愛(ài)戀哪里找。掌燈研墨訴煩惱撩嚼。 月滿星稀一人嘆停士,涼風(fēng)弱柳水拍岸。撫琴?gòu)棻M相思斷完丽。
    陳夢(mèng)隱閱讀 190評(píng)論 0 0