接著分析DispatchServlet,按照慣例蟆融,依舊貼出一張清晰醒腦的大圖绰播,這張圖清晰的描繪了關于DispatchServlet的類繼承關系仗颈,如下:
還是先從web.xml的配置說起,從上面的關系圖中我們可以看出DispatchServlet實際也是一個實現(xiàn)了Servlet標準接口的類诸尽,所以要想web應用正常跑原杂,我們必須在web.xml中注冊這個Servlet,如圖:
在開始分析DispatchServlet之前您机,我想跟大家細細的講講Servlet穿肄。Servlet主要是用來處理客戶端請求并封裝響應結果通過其內部的service方法發(fā)送給客戶端,如圖中所示:
其實從這張圖就可以看出际看,Sevlet是有一個生命周期的咸产,并且它從創(chuàng)建到銷毀都是在一個Servlet容器中進行的。在SpringMVC中Servlet的框架是由兩個java包組成的:javax.servlet和javax.servlet.http仲闽;javax.servlet中定義了一套標準化的servlet類都必須實現(xiàn)或者擴展的通用接口和類脑溢。javax.servlet.http中定義了基于Http通訊的實現(xiàn),當Web容器接收到servlet請求時赖欣,不管是get屑彻,put還是別的,它會把請求封裝成一個個的HttpServletRequest對象顶吮,然后傳給Servlet#service方法進行處理社牲,最后返回一個HttpServletResponse對象返回。關于Servlet容器的銷毀悴了,其實servlet容器會先調用servlet#destroy方法搏恤,先銷毀servlet對象以及與其關聯(lián)的ServletConfig對象汗菜,最后再實現(xiàn)容器的關閉。
擴展點:從一開始的那種類繼承關系圖中我們可以看出挑社,如果我們想定義我們自己的Servlet對象陨界,我們完全可以學著SpringMVC的方式繼承HttpServlet,但切記寫完之后要在web.xml中注冊痛阻。
1.關于DispatchServlet的初始化
按照套路菌瘪,我們得先看HttpServlet的Init方法,因為這個Init方法是Servlet容器記載Servlet類的數據到內存中的開始阱当,它首先會創(chuàng)建一個ServletConfig對象俏扩,然后在創(chuàng)建一個Servlet對象,最后調用這個Init方法將Servlet對象與ServletConfig關聯(lián)起來進行初始化的操作弊添,而在SpringMVC中這個方法的實現(xiàn)在HttpServletBean中录淡,如圖:
看下其HttpServletBean#Init方法的邏輯:
實現(xiàn)流程如下:
1.獲取在web.xml中配置servletConfig參數(也就是在init-param配置項中的參數)的并且進行封裝及驗證,通過校驗missingProps來驗證某些參數的必要性油坝,如果有些missingProps項沒有初始化的話嫉戚,將拋出異常(內部細節(jié)讀者可自行查閱);
2.將當前這個servlet轉為BeanWrapper澈圈,使Spring對其能進行一些屬性的填充彬檀;
3.通過ResourceLoader對配置資源進行解析(也就是我們在web.xml中配置的springmvc的配置文件);
4.InitServletbean方法留給子類擴展的瞬女,其主要在FrameworkServlet進行實現(xiàn)窍帝,也就是為了對servletBean進行初始化;
InitServletbean主要干了兩件事诽偷,初始化WebApplicationContext以及FrameworkServlet坤学。
2.WebApplicationContext的初始化
來看initWebApplicationContext,貼上代碼报慕,一步一步分析:
流程如下:
1.先去根據servletContext對象獲取最上層的WebApplicationContext深浮,這個地方不懂的請看之前寫過的SpringMVC流程全解析(1);
2.判斷當前WebApplicationContext是否被創(chuàng)建過,如果是卖子,那么將其設置到rootContext略号,并且對已經創(chuàng)建的WebApplicationContext進行配置及刷新(這一步咋們在后面細說);
3.如果沒有被創(chuàng)建過刑峡,那么通過servlet的contextAttribute查找ServletContext中對于的屬性洋闽,默認是WebApplicationContext.class.getName()+".ROOT",也就是ContextLoaderListener加載時創(chuàng)建的XmlWebApplicationContext實例來進行查找突梦。
擴展:這個地方我們其實可以重寫這個初始化的邏輯來創(chuàng)建我們自己的WebApplicationContext诫舅,但是必須要在web.xml中創(chuàng)建初始化contextAttribue的key值,SpringMVC中默認是WebApplicationContext.class.getName()+".ROOT"宫患;
如果上面還是沒法獲取到刊懈,那就只能根據ContextLoaderListener創(chuàng)建的rootContext進行創(chuàng)建了。通過servlet的初始化參數contextClass,如果沒有配置的話就是XmlWebApplicationContext.class虚汛,然后通過反射實例化WebApplicationContext匾浪,然后將其與ContextLoaderListener創(chuàng)建的XmlWebApplicationContext關聯(lián),同時獲取contextConfigLocation屬性卷哩,并配置在servlet初始化參數中蛋辈。最后根據創(chuàng)建的XmlWebApplicationContext初始化Spring環(huán)境,加載配置文件将谊。
4.使用AbstractApplicationContext的refresh方法進行配置文件的加載冷溶,這是個模板方法,實現(xiàn)邏輯在DispatchServlet中尊浓,主要是根據WebApplicationContext刷新spring在web功能中所必須使用的全局變量逞频。
到了這一塊,我們得結合著我們得SpringMVC配置文件進行講解了栋齿,我得讓你清晰的知道為啥SpringMVC配置文件就得這么配置苗胀。
1.initMultipartResolver,initLocaleResolver瓦堵,initThemeResolver
這是SpringMVC用來處理文件上傳的柒巫,當然,要使用它谷丸,并不是以下這一處配置堡掏,你還必須在Web應用的上下文中添加對應的MultipartResolver,也是是在controller的方法開頭添加刨疼,這樣springMVC才會對它進行處理泉唁。代碼如下:
我們的配置文件內容:
簡單解釋下,CommonsMultipartResolver是SpringMVC默認的文件解析器揩慕,如果要想自定義亭畜,繼承這個類做些實現(xiàn)即可,關于property屬性迎卤,這些都在CommonsFileUploadSupport定義好的拴鸵,CommonsMultipartResolver繼承了CommonsFileUploadSupport這個類。
關于initLocaleResolver蜗搔,initThemeResolver與initMultipartResolver實現(xiàn)方式基本大同小異劲藐,SpringMVC都有默認的實現(xiàn)類,查看DispatchServlet.properties可以看到樟凄。
2.HandlerMapping
當客戶端發(fā)出請求時聘芜,DispatchServlet會將請求提交給HandlerMapping,然后HandlerMapping根據WebApplicationContext的配置來回傳給DispatchServlet相應的Controller缝龄,按照慣例汰现,依舊按照代碼講述挂谍。
1.默認情況下,它會去加載當前系統(tǒng)中所有實現(xiàn)了HandlerMapping接口的bean瞎饲,如圖所示:
2.但是我們可以在web.xml文件中配置detectAllHandlerMappings為false口叙,那樣就會只加載我們在Springmvc配置文件中加載的HandlerMapping了。
此時嗅战,SpringMVC將會查找為handlerMapping的bean庐扫,作為當前系統(tǒng)唯一的handlerMapping。
如果沒有定義的話仗哨,那么就會去找在DispatchServlet.properties文件中定義的HandlerMapping了形庭,SpringMVC默認是以BeanNameUrlHandlerMapping作為映射策略的實現(xiàn)。
擴展點:這個地方其實可以通過重寫DispatchServlet#getDefaultStrategies自定義HandlerMapping策略厌漂。
好了萨醒,今天就講到這里,最后總結下苇倡,其實springMVC流程看起來還是很清晰富纸,但是我們要學會透過這個流程去看內部的實現(xiàn),去看SpringMVC中一些好的設計點旨椒,好的實現(xiàn)方式晓褪。這些我決定在寫完這個系列文章的最后給大家再總結。