spring mvc 原理深度解析(一)

1议纯、回顧servlet 與jsp 執(zhí)行過程

2苏潜、Spring MVC請求處理流程

3步责,mvc 體系結(jié)構(gòu)詳解

URL映射

表單參數(shù)映射

調(diào)用目標(biāo)Control

數(shù)據(jù)模型映射

視圖解析

異常處理

DispatcherServlet

DispatcherServlet它的作用有點兒類似于網(wǎng)關(guān)挺尾,DispatcherServlet負(fù)責(zé)將請求轉(zhuǎn)發(fā)到不同的Controller上去鹅搪。

配置DispatcherServlet

/ 后面不能寫成/*否則容易出問題。

編寫Controller遭铺,繼承controller類丽柿。實現(xiàn)handlerRequest接口。指定ModelAndView魂挂。這是一種方式甫题。

第二種方式 繼承 HttpRequestHandler類。那么DispatcherServlet是如何去匹配不同的controller的呢涂召?這就需要了解SpringMVC的體系結(jié)構(gòu)坠非。整個體系結(jié)構(gòu)都搞懂才行。下面就從一個全局的視角去看Spring MVC的體系結(jié)構(gòu)果正。

SpringMVC的體系結(jié)構(gòu)圖

DispatcherServlet是通過HandlerMapping的一組映射關(guān)系去找到我們的目標(biāo)Controller

通過上面我們知道Controller存在的形式是多種多樣的炎码。可以通過Controller接口的形式秋泳,也可以通過@Controller注解的形式存在潦闲。等等等。迫皱。歉闰。有這么多不同的Controller形式DispatcherServlet是如何去執(zhí)行的呢?這里面就涉及到一個HandlerAdapter控制器適配器舍杜。找到數(shù)據(jù)模型的一個映射新娜。然后試圖解析是通過ViewResolver這個東東。他支持各種各樣的試圖解析既绩。view用于具體的試圖解析概龄。HandlerExceptionResolver異常攔截解析器。

下面就圍繞這張圖去逐一進行源碼分析饲握。

一私杜,HandlerMapping源碼分析

HandlerMapping是一個接口蚕键,他有兩個實現(xiàn)類,MatchableHandlerMapping和AbstractHandlerMapping衰粹。HandlerMapping在getHandler里面并沒有返回handler(我們的目標(biāo)執(zhí)行器锣光,也就是我們的controller)而是返回一個執(zhí)行鏈條。HandlerExecutionChain铝耻。

根據(jù)下圖可知HandlerExecutionChain里面有一個getHandler方法誊爹,這里面返回了Handler。但是具體的是哪個一個Handler是不確定的瓢捉,因為他是Object類型的频丘。所以通過動態(tài)代理的方式是不能實現(xiàn)的。只能通過這個執(zhí)行鏈條去返回目標(biāo)Handler泡态。后續(xù)的文章我們會對執(zhí)行鏈條做深入的講解搂漠。

然后看上面的類繼承關(guān)系圖。左面繼承了AbstractUrlHandlerMapping其中AbstractDetectingUrlHandlerMapping起到自動發(fā)現(xiàn)的作用某弦,他是根據(jù)Bean的名稱桐汤,而且是必須以/開頭。比如這樣

<bean name="/hello.do" class="com.tuling.control.SimpleControl"/>

然后SimpleUrlHandlerMapping靶壮。如果不需要自動發(fā)現(xiàn)功能怔毛,則使用這個。SimpleUrlHandlerMapping是自己配置URL和controller之間的一個關(guān)系亮钦。所以他們之間的區(qū)別就是一個自動發(fā)現(xiàn)馆截,一個手動配置。AbstractHandlerMethodMapping是對方法進行映射RequestMappingInfoHandlerMapping這個大家比較熟悉了蜂莉,就是通過@RequestMapping進行映射的蜡娶。如果配置了SimpleUrlHandlerMapping或者BeanNameUrlHandlerMapping那么默認(rèn)的就會失效。SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping可以同時配置映穗,都可以映射到我們的Controller控制器窖张。

SimpleUrlHandlerMapping的初始化執(zhí)行流程

對initApplicationContext()方法進行調(diào)用。其中super.initApplicationContext();這里是初始化一些攔截器蚁滋。比如自動發(fā)現(xiàn)攔截器啊等等等宿接。this.registerHandlers(this.urlMap);開始進行Url的匹配點進去看一下可知如果urlMap不為空才進行匹配。第一步是!url.startsWith("/")如果不是/開頭的辕录。就會把你的URL加上/然后獲取Handler睦霎,就是我們的控制器。然后判斷handler是不是String類型的走诞,副女。如果是進行去除空格的處理。然后執(zhí)行this.registerHandler(url, handler);點進去發(fā)現(xiàn)蚣旱,將handler轉(zhuǎn)化為Object類型碑幅。然后判斷是否是懶加載并且是String類型戴陡。然后將Handler轉(zhuǎn)化成String的字符串。然后獲取一個ApplicationContext對象沟涨。判斷handler是都是單例恤批。如果是通過ApplicationContext.getBean(handlerName)獲取到一個Object對象。然后獲取handlerMap裹赴,判斷是否是空喜庞,如果不為空說明你配置了兩個name屬性此時會拋出異常。接著判斷你的url是否等于/如果是設(shè)置一個根目錄的Handler通過localhost:8080/即可訪問我們的handler篮昧。然后判斷是否等于/*如果是就設(shè)置一個默認(rèn)的和Handler赋荆。也就是說你永遠(yuǎn)都不會擔(dān)心找不到這個url的異常笋妥。

最后如果以上情況都不是懊昨,則通過this.handlerMap.put(urlPath, resolvedHandler);將url和handler綁定在一起。如果說handler是懶加載那么此時map中存儲的value就是beanName春宣。這就是SimpleUrlHandlerMapping的初始化流程酵颁。

SimpleUrlHandlerMapping的訪問執(zhí)行流程

我們知道最終執(zhí)行調(diào)用的一定是DispatcherServlet。所以我們從這里開始入手月帝。找到getHandler

首先通過Iterator var2 =this.handlerMappings.iterator();或取到所有的Mapping然后遍歷躏惋。遍歷一次取出一個handlerMapping然后調(diào)用handlerMapping。點進去看看嚷辅。第一行g(shù)etHandlerInternal這個方法很重要簿姨。點進去看看,在這個方法里第一行通過getUrlPathHelper().getLookupPathForRequest(request);去解析我們地址欄上的URL簸搞。比如或取到的是/hello.do扁位。第二行通過Object handler = lookupHandler(lookupPath, request);去查找對應(yīng)的handler。點進去發(fā)現(xiàn)就是從上面put進去的handlerMap找到對應(yīng)的handler趁俊。在最后return一個buildPathExposingHandler域仇。點進去發(fā)現(xiàn)HandlerExecutionChain這里面配置了一個攔截器鏈,那么返回的并不是我們最終的handler寺擂。

那么getHandlerInternal方法的第二行也執(zhí)行完了暇务。最終拿到得是一個攔截器鏈,如果說攔截器鏈為空則判斷第一行的url解析器拿到得/hello.do是否等于/如果是Object rawHandler=getRootHandler()設(shè)置為根節(jié)點的handler怔软,然后判斷rawHandler是否等于空垦细,如果是則調(diào)用默認(rèn)的handler。如果不等于空呢挡逼,則直接通過beanName從Spring容器找到Handler括改。 再調(diào)用buildPathExposingHandler。還是獲取一個攔截器鏈挚瘟。最終叹谁,將攔截器鏈返回饲梭。getHandlerInternal方法執(zhí)行結(jié)束。返回到getHandler方法里焰檩。那么如果說憔涉,沒有獲取到攔截器鏈,則獲取默認(rèn)的handler析苫。如果不為空兜叨,則直接通過beanName從容器中獲取到handler。然后衩侥,去創(chuàng)建一個攔截器鏈條国旷,又加入了一個攔截器。最后進一步獲取handler茫死。然后返回HandlerExecutionChain跪但。最后通過HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());獲取到的才是我們的handler。SimpleUrlHandlerMapping執(zhí)行流程到此介紹完畢峦萎,說實話SpringMVC的代碼寫的跟IOC\aop差著一大塊兒的水平感覺屡久。

Controller 接口:

HttpRequestHandler 接口:

HttpServlet 接口:

@RequestMapping方法注解

可以看出 Handler 沒有統(tǒng)一的接口,當(dāng)dispatchServlet獲取當(dāng)對應(yīng)的Handler之后如何調(diào)用呢爱榔?調(diào)用其哪個方法被环?這里有兩種解決辦法,一是用instanceof 判斷Handler 類型然后調(diào)用相關(guān)方法 详幽。二是通過引入適配器實現(xiàn)筛欢,每個適配器實現(xiàn)對指定Handler的調(diào)用。spring 采用后者唇聘。

HandlerAdapter詳解

HandlerAdapter中有三個接口

1supports接口版姑,主要的作用是傳入一個handler看看是否可以被執(zhí)行。如果可以返回true雳灾。

2handle處理接口漠酿,處理完返回一個ModelAndView。

3getLastModified谎亩,用作緩存處理炒嘲,獲取最后一次修改時間。

我們知道HandlerAdapter對應(yīng)適配了4種handler關(guān)系圖如下匈庭。

舉個例子夫凸,如果說我們寫了一個Servlet,然后繼承HttpServlet此時如果不配置SimpleServletHandlerAdapter適配器阱持,那么就會報錯夭拌,報500的異常。但是如果配置了這個適配器,那么SpringMVC給我們配置的默認(rèn)適配器SimpleControllerHandlerAdapter就會失效鸽扁。所以需要手動的配置一下蒜绽。下面看一下源碼即可驗證這個說法。上面講到返回一個攔截器鏈條桶现。然后下面的代碼就是獲取適配器如下圖躲雅。

點進getHandlerAdapter方法。這個方法里做了一個do-while循環(huán)骡和。通過support方法判斷是否是可用的適配器相赁。

在下圖的方法里判斷是否是我們配置的適配器,如果是則返回true慰于。

拿到適配器以后就開始了真正的調(diào)用钮科。

此時適配器找到了以后就開始真正的調(diào)用handler也就是我們servlet或者是controller。然后通過一個叫ViewResolver的接口類去解析婆赠。其中有一個接口叫resolveViewName绵脯。他返回一個View。然后View里面有一個render接口页藻。通過里面的model進行處理桨嫁,封裝HTML。通過response進行寫入份帐。所以它是先有ViewResolver才有的View。

然后看看View有哪些實現(xiàn)類如下圖楣导。

第一個AbstractTemplateViewResolver里面有以下兩種視圖支持废境,比如我們常用的FreeMarkerViewReslover。

第二個BeanNameViewResolver可以通過這種方式實現(xiàn)自定的視圖解析器筒繁,生明一個自定義View類噩凹。然后實現(xiàn)View接口。在render里面去做返回毡咏。然后controller里面通過ModelAndView視圖解析器的構(gòu)造器去加載我們的自定義View類型驮宴。

第三個就是InternalResourceViewResolver。這個就是我們最常用的呕缭,通過配置前綴堵泽,后綴等信息去實現(xiàn)視圖解析。

下圖是通過BeanName的形式做的視圖解析

最后附上一張整體的流程圖

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恢总,一起剝皮案震驚了整個濱河市迎罗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌片仿,老刑警劉巖纹安,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡厢岂,警方通過查閱死者的電腦和手機光督,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來塔粒,“玉大人可帽,你說我怎么就攤上這事〈芭” “怎么了映跟?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扬虚。 經(jīng)常有香客問我努隙,道長,這世上最難降的妖魔是什么辜昵? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任荸镊,我火速辦了婚禮,結(jié)果婚禮上堪置,老公的妹妹穿的比我還像新娘躬存。我一直安慰自己,他們只是感情好舀锨,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布岭洲。 她就那樣靜靜地躺著,像睡著了一般坎匿。 火紅的嫁衣襯著肌膚如雪盾剩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天替蔬,我揣著相機與錄音告私,去河邊找鬼。 笑死承桥,一個胖子當(dāng)著我的面吹牛驻粟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凶异,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼蜀撑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了唠帝?” 一聲冷哼從身側(cè)響起屯掖,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎襟衰,沒想到半個月后贴铜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年绍坝,在試婚紗的時候發(fā)現(xiàn)自己被綠了徘意。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡轩褐,死狀恐怖椎咧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情把介,我是刑警寧澤勤讽,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站拗踢,受9級特大地震影響脚牍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜巢墅,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一诸狭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧君纫,春花似錦驯遇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至双吆,卻和暖如春眨唬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背好乐。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瓦宜,地道東北人蔚万。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像临庇,于是被迫代替她去往敵國和親反璃。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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

  • 1假夺、回顧servlet 與jsp 執(zhí)行過程 2淮蜈、Spring MVC請求處理流程 3,mvc 體系結(jié)構(gòu)詳解 URL...
    夕陽殘缺的如此唯美閱讀 770評論 0 0
  • 1.Spring整體架構(gòu) 1)核心容器(Core Container) Core模塊已卷,主要包含了Spring框架基...
    Sponge1128閱讀 1,051評論 0 1
  • 16. Web MVC 框架 16.1 Spring Web MVC 框架介紹 Spring Web 模型-視圖-...
    此魚不得水閱讀 1,017評論 0 4
  • 前言 對于Spring MVC項目搭建相信大家按照網(wǎng)上教程來做基本都會梧田,但更多時候我們應(yīng)該多問幾個為什么,多思考實...
    九風(fēng)萍舟閱讀 2,736評論 0 12
  • 春節(jié)假期已經(jīng)結(jié)束,新年的氣氛已經(jīng)漸遠(yuǎn)裁眯,小時候盼著新年鹉梨、長大后卻是覺得年味的沉重,從新年的:‘學(xué)習(xí)怎么樣…’到如今的...
    賀銀瑩閱讀 200評論 0 0