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