Spring MVC源碼閱讀

spring mvc 請求處理流程
SpringMVC核心處理流程:

1川无、DispatcherServlet前端控制器接收發(fā)過來的請求,交給HandlerMapping處理器映射器

2枕稀、HandlerMapping處理器映射器,根據(jù)請求路徑找到相應(yīng)的HandlerAdapter處理器適配器(處理器適配器就是那些攔截器或Controller)

3、HandlerAdapter處理器適配器荞估,請求數(shù)據(jù)綁定和轉(zhuǎn)換貌笨,處理一些功能請求弱判,返回一個ModelAndView對象(包括模型數(shù)據(jù)、邏輯視圖名)

4锥惋、ViewResolver視圖解析器昌腰,先根據(jù)ModelAndView中設(shè)置的View解析具體視圖

5、然后再將Model模型中的數(shù)據(jù)渲染到View上

這些過程都是以DispatcherServlet為中軸線進(jìn)行的膀跌。

1.入口源碼

springMVC的請求會交由dispatcherServlet處理遭商,其本質(zhì)上是一個多線程的請求處理機(jī)制;核心業(yè)務(wù)邏輯被設(shè)計(jì)在doDispatcher(..)方法中捅伤;

/**
 * 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);
 
          // 綁定url -> 具體的handler/Controller.method(..)
         // Determine handler for the current request.
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null || mappedHandler.getHandler() == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
 
         // 根據(jù)handler綁定具體的adapter
         // 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;
            }
         }
 
         // interceptor 攔截器進(jìn)行前置預(yù)處理
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }
 
         // 在handle方法中進(jìn)行了請求數(shù)據(jù)的綁定劫流,方法invoke,返回model的處理和封裝等
         // 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);
         }
      }
   }
}

2.核心關(guān)注的幾個問題

2.1 請求如何路由到具體的Controller上的方法進(jìn)行處理?

根據(jù)請求路徑丛忆,與已知的handlerMapping進(jìn)行匹配祠汇,并加入interceptors:

dispatcherServlet.getHandler()最終調(diào)用AbstracteHandlerMapping.getHandlerExecutionChain(..)中
進(jìn)行url與handlerMapping進(jìn)行匹配,并加入interceptors;

2.2 扁平化的前端請求數(shù)據(jù)如何進(jìn)行數(shù)據(jù)綁定熄诡?

我們知道可很,前端的get/post等請求,會被requestServlet接受凰浮,并封裝成HttpServletRequest的parameterMap中我抠,每一項(xiàng)請求的數(shù)據(jù)結(jié)構(gòu)都是 K-V 形的姜骡。

而我們知道,像這樣的形式屿良,
image.png

那么在這個中間數(shù)據(jù)如何實(shí)現(xiàn)數(shù)據(jù)綁定到Bean圈澈,String格式的Value 轉(zhuǎn)換成各種目標(biāo)格式。

事實(shí)上尘惧,spring MVC將整個網(wǎng)絡(luò)請求的處理流程進(jìn)行了合理的切分,其大致的處理流程如下:

1.調(diào)用匹配到的adapter.handle(..)

2.然后調(diào)用invokeAndHandle(..)

3.調(diào)用invokeForRequest(..)獲取getMethodArgumentValues(..)獲取和綁定入?yún)?

4.在具體的方法中康栈,獲取支持處理的argumentResolvers,然后調(diào)用resolveArgument(..)方法;

5.在ModelAttributeMethodProcessor中調(diào)用bindRequestParameters(binder, webRequest),方法,然后再調(diào)用bind()進(jìn)行bean參數(shù)的綁定喷橙;

6.調(diào)用binder.convertIfNecessary(arg, parameter.getParameterType(), parameter)方法進(jìn)行參數(shù)的轉(zhuǎn)換啥么;
數(shù)據(jù)綁定調(diào)用堆棧
請求參數(shù)最初都是扁平化的
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市贰逾,隨后出現(xiàn)的幾起案子悬荣,更是在濱河造成了極大的恐慌,老刑警劉巖疙剑,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件氯迂,死亡現(xiàn)場離奇詭異,居然都是意外死亡言缤,警方通過查閱死者的電腦和手機(jī)嚼蚀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來管挟,“玉大人轿曙,你說我怎么就攤上這事∑ⅲ” “怎么了导帝?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長穿铆。 經(jīng)常有香客問我您单,道長,這世上最難降的妖魔是什么悴务? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任睹限,我火速辦了婚禮,結(jié)果婚禮上讯檐,老公的妹妹穿的比我還像新娘羡疗。我一直安慰自己,他們只是感情好别洪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布叨恨。 她就那樣靜靜地躺著,像睡著了一般挖垛。 火紅的嫁衣襯著肌膚如雪痒钝。 梳的紋絲不亂的頭發(fā)上秉颗,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機(jī)與錄音送矩,去河邊找鬼蚕甥。 笑死,一個胖子當(dāng)著我的面吹牛栋荸,可吹牛的內(nèi)容都是我干的菇怀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼晌块,長吁一口氣:“原來是場噩夢啊……” “哼爱沟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起匆背,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤呼伸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后钝尸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體括享,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年蝶怔,在試婚紗的時候發(fā)現(xiàn)自己被綠了奶浦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兄墅。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡踢星,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出隙咸,到底是詐尸還是另有隱情沐悦,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布五督,位于F島的核電站藏否,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏充包。R本人自食惡果不足惜副签,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望基矮。 院中可真熱鬧淆储,春花似錦、人聲如沸家浇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钢悲。三九已至点额,卻和暖如春舔株,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背还棱。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工载慈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人珍手。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓娃肿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親珠十。 傳聞我的和親對象是個殘疾皇子料扰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

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