SpringBoot源碼-mvc工作流程(中)

DispatcherServlet 處理流程

上一節(jié)講了Spring容器啟動(dòng),會(huì)把url與類方法的映射關(guān)系保存起來侄榴,這一節(jié)允扇,就能看到它的作用啦之众。

DispatcherServlet是整個(gè)SpringMVC的核心,負(fù)責(zé)請(qǐng)求處理以及返回響應(yīng)的工作蝇率。直奔主題迟杂,找到DispatcherServlet類,再找到該類下的doService()方法本慕,關(guān)鍵代碼:

try {
    doDispatch(request, response);
}

點(diǎn)擊這個(gè)孤獨(dú)的方法排拷,進(jìn)去,上核心馬:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    // 定義handler執(zhí)行鏈锅尘,重要
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    ModelAndView mv = null;
    // 檢查是否是文件請(qǐng)求
    processedRequest = checkMultipart(request);
    multipartRequestParsed = (processedRequest != request);

    // 獲取當(dāng)前請(qǐng)求的handler
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null) {
        noHandlerFound(processedRequest, response);
        return;
    }

    // 獲取當(dāng)前請(qǐng)求的適配器
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

    // 判斷get方法是否有修改监氢,沒有直接返回
    String method = request.getMethod();
    boolean isGet = HttpMethod.GET.matches(method);
    if (isGet || HttpMethod.HEAD.matches(method)) {
        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
        if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
         return;
        }
    }
    // 前置處理
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
    }

    // hander實(shí)際執(zhí)行
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    applyDefaultViewName(processedRequest, mv);
    // handler后置處理
    mappedHandler.applyPostHandle(processedRequest, response, mv);
    // 處理分發(fā)后的結(jié)果
    processDispatchResult(processedRequest, response, mappedHandler, mv,     
                          dispatchException);

}

看到那個(gè)閃亮的方法沒 - getHandler(processedRequest),點(diǎn)擊進(jìn)去鉴象,上馬:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            // 從handlerMappings 獲取并構(gòu)建請(qǐng)求的執(zhí)行鏈
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

打個(gè)debug忙菠,看下handlerMappings里面是啥

RequestMappingHandlerMapping.png

看到?jīng)],這不就是上一節(jié)講到的東東咩纺弊?

再點(diǎn)擊 mapping.getHandler(request)方法進(jìn)去牛欢,看看有啥:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 獲取handler
    Object handler = getHandlerInternal(request);
    ……
        
    // 是beanName獲取對(duì)應(yīng)的bean
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = obtainApplicationContext().getBean(handlerName);
    }
    ……
        
    // 構(gòu)建handler的執(zhí)行鏈
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);  
    ……

    return executionChain;
}

getHandlerInternal(request) 是重點(diǎn),點(diǎn)進(jìn)去:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 獲取訪問路徑淆游,如 /test/hello
    String lookupPath = initLookupPath(request);
    this.mappingRegistry.acquireReadLock();
    try {
        // 查找訪問路徑對(duì)應(yīng)的處理方法
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}

快到了傍睹!繼續(xù)點(diǎn)擊 lookupHandlerMethod(lookupPath, request)隔盛,進(jìn)入:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    // 根據(jù)訪問路徑匹配請(qǐng)求方法
    List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
    if (directPathMatches != null) {
        // 根據(jù)找到的信息,封裝 matches
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    }
    
    // 省略查找最近匹配內(nèi)容
    ……
    
    // 返回 handlerMethod
    request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
    handleMatch(bestMatch.mapping, lookupPath, request);
    return bestMatch.getHandlerMethod();
}

點(diǎn)擊this.mappingRegistry.getMappingsByDirectPath(lookupPath)拾稳,進(jìn)入:

public List<T> getMappingsByDirectPath(String urlPath) {
    return this.pathLookup.get(urlPath);
}

看到了沒吮炕,pathLookup登場了,T 類型是 RequestMappingInfo访得。

這一步龙亲,就能找到 handlerMethod了,也就是訪問路徑對(duì)應(yīng)的處理方法悍抑。所以上面的getHandler() 方法就能返回對(duì)應(yīng)的 HandlerExecutionChain鳄炉。

回到 doDispatch()方法中,既然拿到了 handlerMethod搜骡,后面就是做了一些錦上添花的事拂盯,最后調(diào)用該方法了。

不講了记靡,口渴谈竿,飲杯開水去~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市摸吠,隨后出現(xiàn)的幾起案子空凸,更是在濱河造成了極大的恐慌,老刑警劉巖寸痢,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劫恒,死亡現(xiàn)場離奇詭異,居然都是意外死亡轿腺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門丛楚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來族壳,“玉大人,你說我怎么就攤上這事趣些》戮#” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵坏平,是天一觀的道長拢操。 經(jīng)常有香客問我,道長舶替,這世上最難降的妖魔是什么令境? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮顾瞪,結(jié)果婚禮上舔庶,老公的妹妹穿的比我還像新娘抛蚁。我一直安慰自己,他們只是感情好惕橙,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布瞧甩。 她就那樣靜靜地躺著弥鹦,像睡著了一般肚逸。 火紅的嫁衣襯著肌膚如雪朦促。 梳的紋絲不亂的頭發(fā)上苍鲜,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天混滔,我揣著相機(jī)與錄音,去河邊找鬼油湖。 笑死领跛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的喊括。 我是一名探鬼主播矢棚,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼蒲肋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了申窘?” 一聲冷哼從身側(cè)響起孔轴,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤路鹰,失蹤者是張志新(化名)和其女友劉穎牵寺,沒想到半個(gè)月后帽氓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俩块,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡玉凯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年漫仆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片署照。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡建芙,死狀恐怖懂扼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赶熟,我是刑警寧澤陷嘴,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布罩旋,位于F島的核電站眶诈,受9級(jí)特大地震影響逝撬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宪潮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望食磕。 院中可真熱鬧喳挑,春花似錦、人聲如沸单绑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至弯屈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間厅缺,已是汗流浹背宴偿。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留窥妇,地道東北人娩践。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓翻伺,卻偏偏與公主長得像吨岭,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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