SpringMVC之三個Servlet

三個Servlet

一、HttpServletBean

參與創(chuàng)建工作,沒有涉及請求的處理

二傻盟、FrameworkServlet

當?shù)谝粋€請求過來時,就初始化容器

init.png

如上圖嫂丙,當?shù)谝粋€請求過來時娘赴,F(xiàn)rameworkServlet就初始化容器,并將Spring的容器進行刷新跟啤,而DispatcherServlet的onRefresh刷新只是初始化九大組件诽表。以后的請求就都不需要初始化容器媳叨。
初始化完容器以后,因為FrameworkServlet重寫了Servlet的service关顷,doGet糊秆,doPost等方法,所以會先走到Framework的doGet或者doPost等請求方法议双。
FrameworkServlet的設(shè)計很有趣痘番,它將所有的請求get,post,delete等請求全部嫁接到processRequest(request,response)
processRequest.png

processRequest(request,response)方法中又調(diào)用了doService(request,response)方法,該方法是在DispatcherServlet中具體實現(xiàn)
所以說FrameworkServlet主要有兩個作用:
1.創(chuàng)建并初始化Spring容器
2.請求的入口

三平痰、DispatcherServlet

第一個作用是初始化九大組件汞舱,就是在上面的FrameworkServlet執(zhí)行完初始化容器后,就調(diào)用onRefresh宗雇,方法里面只調(diào)用了initStrategies(context)方法昂芜,initStrategies方法才是重點,如下圖

nine_components.png

每個組件在處理請求時都有可能會用到

第二個作用就是處理請求
請求的入口是doService 赔蒲,我們看下請求過來的調(diào)用鏈

image.png

doService對request設(shè)置了一些屬性
doService.png

接著調(diào)用doDispatch泌神,doDispatch是DispatcherServlet的核心
我們看代碼

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        /**
         *
         *   doDispatch中最重要的代碼總共有四句(見下面的標簽數(shù)字)
         *
         *   Handler、HandlerMapping舞虱、HandlerAdapter三者的關(guān)系:
         *      Handler:就是我們的控制器Controller中加了@XXXMapping的方法
         *      HandlerMapping: 用來快速查找Handler
         *      HandlerAdapter:調(diào)用Handler來干活欢际,而且不同Handler需要不同的Adapter
         *   這就好比HandlerAdapter是工人,Handler是工具矾兜,HandlerMapping是根據(jù)加工的需求來選擇用
         *   什么設(shè)備
         */

        /**
         * 封裝Request损趋,如果不是上傳請求則直接使用接收到的request
         * 如果是上傳請求,重新封裝成MultipartHttpServletRequest
         */
        HttpServletRequest processedRequest = request;
        /**
         * 處理請求的處理器鏈
         * 包含有處理器Handler和對應(yīng)的攔截器Interceptor
         */
        HandlerExecutionChain mappedHandler = null;
        /**
         * 是否為上傳請求的標記
         */
        boolean multipartRequestParsed = false;

        /**
         * 從request中獲取異步請求
         */
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            /**
             * View 跟 ViewResolver
             * View 是用來展示數(shù)據(jù)的
             * 而ViewResolver是用來查找View的
             * 做完請求工作后椅寺,需要返回結(jié)果浑槽,而返回結(jié)果就需要模板,
             * View就是所需要的模板返帕,ViewResolver就是來選擇哪個模板
             *
             * **/
            ModelAndView mv = null;
            /**
             * 異常聲明
             * doDispatch()中對異常又兩種處理方法:
             *      一桐玻、如果是處理請求中出現(xiàn)的異常,會捕獲并在processDispatchResult中渲染到最后的視圖中
             *      二溉旋、如果是渲染中出現(xiàn)異常畸冲,則直接拋出
             */
            Exception dispatchException = null;

            try {
                // 檢查是不是上傳請求
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                /** 第一句
                 *  使用HandlerMapping找到可以干活的Handler
                 *
                 * **/
                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    // 找不到Handler返回404
                    noHandlerFound(processedRequest, response);
                    return;
                }

                /** 第二句
                 *  找到合適的HandlerAdapter去讓他干活
                 * **/
                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // 處理GET、HEAD請求的last-modified
                // Process last-modified header, if supported by the handler.
                /**
                 * Last-Modified是HTTP頭部的一種屬性观腊,表示當前請求的資源跟上一次請求的資源是否相同
                 * 如果相同邑闲,返回304并且沒有新的實體(body)返回
                 * 否則返回新的實體內(nèi)容
                 *
                 * 在瀏覽器第一次請求某一個URL時,服務(wù)器端的返回狀態(tài)會是200梧油,內(nèi)容是客戶端請求的資源苫耸,
                 * 同時有一個Last-Modified的屬性標記此文件在服務(wù)器端最后被修改的時間。
                 * 客戶端第二次請求此URL時,根據(jù)HTTP協(xié)議的規(guī)定烙博,瀏覽器會向服務(wù)器傳送If-Modified-Since報頭,詢問該時間之后文件是否有被修改過
                 * 如果服務(wù)器端的資源沒有變化飞涂,則自動返回 HTTP 304(Not Changed.)狀態(tài)碼嫌褪,內(nèi)容為空呀枢,這樣就節(jié)省了傳輸數(shù)據(jù)量。
                 * 當服務(wù)器端代碼發(fā)生改變或者重啟服務(wù)器時笼痛,則重新發(fā)出資源裙秋,
                 * 返回和第一次請求時類似。從而保證不向客戶端重復發(fā)出資源缨伊,也保證當服務(wù)器有變化時摘刑,客戶端能夠得到最新的資源。
                 */
                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;
                    }
                }

                /** 如果有攔截器刻坊,就餓執(zhí)行我們的攔截器枷恕,preHandle前置處理**/
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                /** 第三句
                 *  讓HandlerAdapter開始干活,干完活后返回數(shù)據(jù)
                 * **/
                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                // 如果需要異步處理谭胚,則直接返回
                /**
                 * 因為異步處理會重新開啟一個線程去執(zhí)行結(jié)果的返回
                 * 不會占用目前這個線程徐块,所以可以直接返回
                 */
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                // 當view為空時(比如,handler返回類型為void)漏益,根據(jù)request設(shè)置默認view
                applyDefaultViewName(processedRequest, mv);
                /** 執(zhí)行了攔截器的后置處理 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);
            }
            /** 第四句
             *  將數(shù)據(jù)處理蛹锰,通過View展示給用戶
             *  處理結(jié)果深胳,包括處理異常绰疤,渲染頁面,發(fā)出完成通知舞终,觸發(fā)攔截器的afterCompletion
             * **/
            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()) {
                // 執(zhí)行異步的攔截器
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // 刪除上傳請求的資源轻庆,不然會產(chǎn)生臨時的資源
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

doDispatch方法主要有四個任務(wù):1.用request找到Handler; 2.根據(jù)Handler找到HandlerAdapter敛劝; 3.用HandlerAdapter處理Handler 4.調(diào)用processDispatchResult方法處理結(jié)果(包括異常的處理和View視圖的渲染)余爆。
除此之外還另外做了許多事情,比如判斷是否為上傳文件請求夸盟,是否帶有Last-Modified頭部蛾方,執(zhí)行攔截器,是否為異步請求...
processDispatchResult方法處理上面doDispatch返回的結(jié)果上陕,包括處理異常桩砰、渲染頁面、觸發(fā)攔截器的后置處理释簿,處理異常只是處理doDispatch()方法中的異常亚隅、渲染頁面的異常就拋出。渲染頁面的方法調(diào)用render

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庶溶,一起剝皮案震驚了整個濱河市煮纵,隨后出現(xiàn)的幾起案子懂鸵,更是在濱河造成了極大的恐慌,老刑警劉巖行疏,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匆光,死亡現(xiàn)場離奇詭異,居然都是意外死亡酿联,警方通過查閱死者的電腦和手機殴穴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來货葬,“玉大人采幌,你說我怎么就攤上這事≌鹜埃” “怎么了休傍?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蹲姐。 經(jīng)常有香客問我磨取,道長,這世上最難降的妖魔是什么柴墩? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任忙厌,我火速辦了婚禮,結(jié)果婚禮上江咳,老公的妹妹穿的比我還像新娘逢净。我一直安慰自己,他們只是感情好歼指,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布爹土。 她就那樣靜靜地躺著,像睡著了一般踩身。 火紅的嫁衣襯著肌膚如雪胀茵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天挟阻,我揣著相機與錄音琼娘,去河邊找鬼。 笑死附鸽,一個胖子當著我的面吹牛脱拼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拒炎,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼挪拟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了击你?” 一聲冷哼從身側(cè)響起玉组,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤谎柄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后惯雳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體朝巫,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年石景,在試婚紗的時候發(fā)現(xiàn)自己被綠了劈猿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡潮孽,死狀恐怖揪荣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情往史,我是刑警寧澤仗颈,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站椎例,受9級特大地震影響挨决,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜订歪,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一脖祈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧刷晋,春花似錦盖高、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蒙幻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胆筒,已是汗流浹背邮破。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留仆救,地道東北人抒和。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像彤蔽,于是被迫代替她去往敵國和親摧莽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

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