摘要:問題來了,我們到底還在用嗎答案是,不全用。后者是初始化的配置,主要是的配置。啟動(dòng)類測試啟動(dòng)項(xiàng)目后,在瀏覽器里面輸入。通過查詢已裝載的,并且支持該而獲取的。按照前面對(duì)的描述,對(duì)于而言,這個(gè)必定是。的核心在的方法中。
之前已經(jīng)分析過了Spring的IOC(《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》)與AOP(《從源碼入手,一文帶你讀懂Spring AOP面向切面編程》)的源碼,本次就來分析下SpringMVC。本文先簡述下目前SpringMVC的使用情況,然后通過Demo的簡單讓大家有一個(gè)初步的使用印象,然后帶著印象去看其中執(zhí)行的分發(fā)源碼。
到底什么是Spring MVC,我們還在用嗎?Spring MVC,官方名字其實(shí)是Spring Web MVC,Maven上的包名也是spring-webmvc。從Spring誕生以來,它就是一款基于Servlet Api的web架構(gòu)。值得一提的是,在Spring5的時(shí)候,出了一款新的Web架構(gòu),F(xiàn)lux,是基于事件驅(qū)動(dòng)模型(類似nodejs)做的。以后會(huì)寫一篇來專門介紹一下Flux,敬請(qǐng)關(guān)注。
MVC,可以說是“上個(gè)世紀(jì)”最流行的前后端交互模型。它包含Model(業(yè)務(wù)模型)、View(用戶視圖)、Controller(控制器),把各部分分開組織,對(duì)代碼抽象與隔離的處理可謂是代碼設(shè)計(jì)的典范。
不過自從15年開始,隨著各種前端框架的崛起,使得前端后端的關(guān)系發(fā)生進(jìn)一步的演變,從MVC架構(gòu)演變成前后端分離的REST架構(gòu)了。以前MVC架構(gòu)每次請(qǐng)求都需要經(jīng)過控制器->模型->視圖的流程,演變成前端請(qǐng)求后端接口,返回JSON的這樣一種REST架構(gòu)。
問題來了,我們到底還在用SpringMVC嗎?答案是,不全用。前后端做了代碼以及部署的分離,也就是說后端并不感知前端的存在,所以對(duì)于后端而言,View(用戶視圖)也就無從可談了。Model(業(yè)務(wù)模型)發(fā)送性質(zhì)上的改變,以前是一個(gè)前端所需要的Model,給頁面讀取,現(xiàn)在是一個(gè)JSON格式給到前端,由前端自由處理。
而作為Web框架的核心,Controller(控制器)則是依然留存的。所以現(xiàn)在大家用SpringMVC用的更多是Controller這一層。當(dāng)然SpringMVC還有其他組件,包括filter、Http Caching、Web Security等等。本文只是著重MVC架構(gòu)中的Controller的功能,而Controller的核心組件則是DispatcherServlet。所以后面我們將通過Demo,來逐步深入了解下,DispatcherSevlet如何做到對(duì)請(qǐng)求控制分發(fā)的。
傳統(tǒng)SpringMVC啟動(dòng)簡述在傳統(tǒng)的SpringMVC中,需要配置web.xml和applicationContext.xml。前者是負(fù)責(zé)配置項(xiàng)目初始化的配置,如servlet、welcome頁面等,是JavaEE的規(guī)范。后者是初始化Spring Context的配置,主要是Bean的配置。
前文說到,SpringMVC是基于Servlet的架構(gòu),而DispatcherServlet則是SpringMVC攔截處理所有請(qǐng)求的Servlet,所以web.xml需要配置DispatcherServlet。其他的還有contextLoaderListener,負(fù)責(zé)加載除DispatcherServlet外的所有context內(nèi)容,另外還需要通過contextConfigLoader指定Spring的配置文件(如applicationContext.xml)。
那么在項(xiàng)目啟動(dòng)的時(shí)候,加載web.xml首先會(huì)執(zhí)行contextLoaderListener,讓它初始化好Spring的Application context。后面有HTTP請(qǐng)求進(jìn)來,則會(huì)落到DispatcherServlet上,讓它去做處理分發(fā)。
SpringBoot Web Demo搭建自從Spring配置注解和SpringBoot誕生以來,越來越少人去寫web.xml和applicationContext.xml配置文件了。但為了方便直接了解Dispatcher的原理,Demo直接用SpringBoot的starter一鍵式搭建。
直接添加web的starter依賴
org.springframework.boot spring-boot-starter-web 2.0.4.RELEASE
看下這個(gè)starter包含什么內(nèi)容
綠框是springMVC的依賴,紅框是Spring自動(dòng)配置的依賴,藍(lán)框則是內(nèi)嵌tomcat的依賴。里面Spring的版本是5.0.8 RELEASE的。
SpringBoot啟動(dòng)類
測試controller
啟動(dòng)項(xiàng)目后,在瀏覽器里面輸入http://localhost:8080/hello?name=Zack。結(jié)果返回Hello Zack。
以上就是我們現(xiàn)在利用SpringMVC的基本內(nèi)容,下面我們來看下SpringMVC如何利用DispatcherServlet做攔截分發(fā)的。
DispatcherServlet源碼分析當(dāng)一個(gè)請(qǐng)求進(jìn)來的時(shí)候,會(huì)先執(zhí)行各種filter,過濾掉最終需要的請(qǐng)求,然后會(huì)落到DispatcherServlet中的doService()方法。該方法是預(yù)先設(shè)置一些特殊請(qǐng)求參數(shù),然后再轉(zhuǎn)發(fā)給doDispatch()做真正的處理轉(zhuǎn)發(fā)。
看一下doDispatch()的注釋說明
該方法的作用就是執(zhí)行實(shí)際分發(fā)到的handler。
Handler通過HandlerMapping的優(yōu)先級(jí)獲取。HandlerAdapter通過查詢DispatcherServlet已裝載的HandlerAdapter,并且支持該Handler而獲取的。
所有的HTTP請(qǐng)求都是doDispatch()去處理的。具體是落到哪個(gè)方法去處理業(yè)務(wù)邏輯,取決于HandlerAdapters或者h(yuǎn)andlers。
從注釋可知,整個(gè)的分發(fā)邏輯核心,就在于HandlerAdapter和Handler。那這兩到底是什么東西?
官網(wǎng)上的說明
HandlerAdapter協(xié)助DispatcherServlet去調(diào)用對(duì)應(yīng)的handler,忽略具體handler是怎么調(diào)用的。例如調(diào)用注解形式的controller需要處理注解,xml配置形式的要解析配置文件。這個(gè)適配器就是為了幫助DispatcherServlet屏蔽掉處理具體的細(xì)節(jié)。
至于Handler沒有清晰解釋,但我們debug源碼可以發(fā)現(xiàn),Handler其實(shí)就是實(shí)際分配到具體需要去處理的方法(對(duì)比下圖紅框和上面Demo的controller)。
回到doDispatch()這個(gè)方法的源碼上,看到getHandler()、getHandlerAdapter()就是獲取Handler和HandlerAdapter所在。
看下getHandler()源碼
整個(gè)方法就那么幾行,不過需要注意有兩個(gè)點(diǎn)。一個(gè)是該方法是返回HandlerExecutionChain類型,而不是一個(gè)Handler。
HandlerExecutionChain其實(shí)就是Handler的一層封裝,還包含Handler對(duì)應(yīng)的interceptor攔截器,用于執(zhí)行Handler的一些前置和后置的操作。
另外一個(gè)點(diǎn),HandlerExecutionChain是按順序遍歷handlerMappings拿出來的。那HandlerMapping又是什么呢?
從官網(wǎng)說明可知,它是一個(gè)請(qǐng)求和handler(實(shí)際是HandlerExecutionChain)的關(guān)聯(lián)Map,通俗的說就是路由與處理邏輯的關(guān)聯(lián)。它主要有兩個(gè)實(shí)現(xiàn),一個(gè)是RequestMappingHandlerMapping(支持注解形式方法),另一個(gè)是SimpleUrlHandlerMapping(維護(hù)顯示注冊(cè)的URI資源)。
由此可推測,在Spring啟動(dòng)的時(shí)候,就會(huì)去掃描注解、注冊(cè)的靜態(tài)資源,從而初始化這個(gè)handlerMappings。具體邏輯就在DispatcherServlet中的initHandlerMappings方法內(nèi)。
初始化的方法內(nèi),主要有三步:
從Spring的ApplicationContext中取出HandlerMapping的Bean
然后對(duì)上面取出來的Bean做優(yōu)先級(jí)排序,主要對(duì)是@Order注解的排序
如果上面取不出Bean,則用默認(rèn)策略。
對(duì)于第三點(diǎn)的默認(rèn)策略,可以找到DispatcherServlet.properties這個(gè)文件,里面配置了一些默認(rèn)HandlerMapping、HandlerAdapter等相關(guān)類。
在初始化handlerMappings后,如果有請(qǐng)求進(jìn)來,后面的request就用請(qǐng)求的路由與HandlerMapping對(duì)比,最后找出Handler(HandlerExecutionChain)。
getHandlerAdapter()在取出實(shí)際處理的Handler后,就需要用它找出support它的適配器(HandlerAdapter)。按照前面對(duì)HandlerAdapter的描述,對(duì)于Demo而言,support這個(gè)Handler必定是RequestMappingHandlerAdapter。
這個(gè)邏輯也非常簡單,同樣是遍歷已初始化的handlerAdapters(初始化的過程類似handlerMappings),然后對(duì)于具體每個(gè)handlerAdapter,調(diào)用其support()方法,看是否支持。
supports()方法也很簡單,就用instanceof判斷handler是否Adapter自己支持的類。
在獲取完Handler和HandlerAdapter后,就可以執(zhí)行HandlerAdapter中的handle方法,其實(shí)際只是調(diào)用Handler的方法。
我們按Demo例子,看下HttpRequestHandlerAdapter的handle()方法實(shí)現(xiàn)。
這個(gè)方法里面就是用HttpServlet的Request和Reponse去調(diào)用我們自己寫的controller里面的方法。需要注意的是,這個(gè)方法返回的是ModelAndView,但我們目前基于Rest架構(gòu)是已經(jīng)不用的了,所以方法返回null回去了。
前面提到Handler是被封裝在HandlerExecutionChain里面的,其中還包含一些前置后置的攔截器。所以在執(zhí)行HandlerAdapter.handle()前后會(huì)有對(duì)HandlerExecutionChain的調(diào)用,執(zhí)行interceptor對(duì)前后置處理的方法
具體里面的實(shí)現(xiàn)就是執(zhí)行interceptor的preHandle()和postHandle()方法。
回過頭來想下,這里的前后置處理會(huì)包括什么呢?在HandlerInterceptor注解上有說明三個(gè)實(shí)現(xiàn)類,分別是UserRoleAuthorizationInterceptor(檢查用戶權(quán)限)、LocaleChangeInterceptor(修改本地時(shí)間)、ThemeChangeInterceptor(修改當(dāng)前主題)。可以看出HandlerInterceptor基本都是對(duì)請(qǐng)求的一些預(yù)處理和結(jié)果封裝。
總結(jié)以上就是SpringMVC中DispatcherServlet的基本過程。下面來總結(jié)下以上內(nèi)容:
前后端的架構(gòu)演變導(dǎo)致SpringMVC的使用發(fā)生改變,更多著重在“C”上了。
“C”的核心在DispatcherServlet的doDispatcher()方法中。
利用request的路由,對(duì)比從已初始化的handlerMappings和handlerAdapters中獲取handler和handlerAdapter。
handler是封裝在HandlerExecutionChain中,其中還包括handler的前后置攔截器。
最后利用適配器模式,調(diào)用HandlerAdapter.handle()方法去執(zhí)行handler具體處理的業(yè)務(wù)邏輯。
在執(zhí)行具體業(yè)務(wù)邏輯前后會(huì)執(zhí)行封裝在HandlerExecutionChain里面的攔截器。
更多技術(shù)文章、精彩干貨,請(qǐng)關(guān)注
博客:zackku.com
微信公眾號(hào):Zack說碼
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/72324.html
摘要:當(dāng)容器啟動(dòng)或終止應(yīng)用時(shí),會(huì)觸發(fā)事件,該事件由來處理。監(jiān)聽器的作用就是啟動(dòng)容器時(shí),自動(dòng)裝配的配置信息。初始化在架構(gòu)中,負(fù)責(zé)請(qǐng)求分發(fā),起到控制器的作用。 ??公司項(xiàng)目使用 struts2 作為控制層框架,為了實(shí)現(xiàn)前后端分離,計(jì)劃將 struts2 切換為 SpringMVC ,因此,這段時(shí)間都在學(xué)習(xí)新的框架,《Spring實(shí)戰(zhàn)》是一本好書,里面對(duì) Spring 的原理實(shí)現(xiàn)以及應(yīng)用都說得很透...
摘要:更多相關(guān)博文參考前一篇博文講了的方式創(chuàng)建應(yīng)用,用過的童鞋都知道,早就沒有什么事情了,其實(shí)的版本,就已經(jīng)支持,不用再寫本篇將介紹下,如何利用取代配置本篇博文,建議和上一篇對(duì)比看,貼出上一篇地址之基于配置的應(yīng)用構(gòu)建構(gòu)建項(xiàng)目依賴對(duì)于依賴 更多spring相關(guān)博文參考: http://spring.hhui.top 前一篇博文講了SpringMVC+web.xml的方式創(chuàng)建web應(yīng)用,用過S...
摘要:第一步在文件中定義前端控制器來攔截用戶請(qǐng)求,在文件中配置文件配置核心部分如下定義前端控制器讓的前端控制器攔截所有請(qǐng)求如果需要一方式提交請(qǐng)求,則定義包含表單數(shù)據(jù)的頁面,如果僅僅是以方式 第一步:在web.xml文件中定義前端控制器 (front controller)DispatcherServlet來攔截用戶請(qǐng)求,在web.xml文件中配置(文件配置核心部分)如下: spri...
摘要:多線程編程這篇文章分析了多線程的優(yōu)缺點(diǎn),如何創(chuàng)建多線程,分享了線程安全和線程通信線程池等等一些知識(shí)。 中間件技術(shù)入門教程 中間件技術(shù)入門教程,本博客介紹了 ESB、MQ、JMS 的一些知識(shí)... SpringBoot 多數(shù)據(jù)源 SpringBoot 使用主從數(shù)據(jù)源 簡易的后臺(tái)管理權(quán)限設(shè)計(jì) 從零開始搭建自己權(quán)限管理框架 Docker 多步構(gòu)建更小的 Java 鏡像 Docker Jav...
閱讀 3427·2021-09-26 09:46
閱讀 2782·2021-09-13 10:23
閱讀 3510·2021-09-07 10:24
閱讀 2387·2019-08-29 13:20
閱讀 2918·2019-08-28 17:57
閱讀 3072·2019-08-26 13:27
閱讀 1174·2019-08-26 12:09
閱讀 505·2019-08-26 10:27