springmvc原始碼閱讀2--dispatcherServlet及談如何找原始碼入口
一、先找到入口:
1、先說找發:
- 根據配置檔案找
這個是最常見的搞法,在原始階段大多數都會使用一些配置檔案來啟動這些框架,但是隨著springboot類似的搞法的流行,這個技巧有點不在那麼起作用,其實原理還是這個。 - 依據j2ee的規範來找:
首先我們要搞明白兩個規範(也是本次會用到的):- web容器在啟動初始化servlet時會呼叫其 init()方法,在接觸請求處理時會呼叫service()方法,在銷燬是會呼叫destory()方法
- web在啟動時會呼叫一系列的監聽器的方法,例如我們下面要說的這個
- 依據啟動的配置:
這個其實有點多餘,我們在看mybatis的時候其實就是這個高的,new SqlSessionFactoryBuilder().build(is); 其實我們要點進去看,會發現build就是其初始化的入口,初始化配置檔案,解析mapper等等…… - 依據j2ee的註解規範來找:
現在大多框架為了簡化配置,則使用了大量的註解,所以也可以通過這個來找,前提是你要對這些註解有了解
2、不多說來找找springmvc的入口
首先當然要看配置檔案呀(當然現在也可以通過註解做到,這裡便不在多做解釋,因為spring環境要通過註解做還是挺費勁,大多數還是配置檔案,這也是sb出現的原因) web.xml
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>springServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:config/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
我們看到配置了一個listener,大家可以看到還是實現的ServletContextListener,這個監聽器是屬於j2ee規範裡面的,在web容器啟動的時候會呼叫ServletContextListener的contextInitialized,那麼這裡面直接debug進去檢視這個方法都幹了什麼initWebApplicationContext(),當然這一分支的體系過於龐大,所以本文的介紹重點還是說下面配置的一個servlet DispatcherServlet。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {}
二、Springmvc:
我們來看一下,我們是如何一步演化到springmvc的程度的
1、springmvc沒有出現的時候:
- 一個請求對應一個servlet
後來大家發現如果應用一旦打起來,這個目錄結構太要命,相信很多初學者搞這個也是十分費勁,下面咱們進化一下。 - 使用一個servlet來做請求,然後做轉發根據請求的名字和方法名字匹配來做轉發呼叫
如:http://localhost:8080/cms/user/login
對應的程式碼
doPost(){
if ("login".equals(methodName)){
login();
}else if(){
}
}
看了這種搞法,是不是覺得很蠢,如果多一個請求就要去改這個類裡面的方法,但是初期的確有人這麼搞過,不幸的是筆者當年也這麼幹過
- 在進化,通過反射來進行單獨處理:
http://localhost:8080/test/UserServlet?method=login
例如這個url,我們可以拿到處理類UserServlet,和要使用的方法login,
Method method UserServlet.class.getMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
String result = (String)method.invoke(UserServlet.class.newInstance(), request,response);
然後在依據返回值來做重定向還是轉發。差不多這個就是BaseServlet的原型了,這個上面是我在markdown手寫的程式碼,只是一個簡要的說明程式碼,之前寫過的筆記招就不知道哪去了,所以這個地方放置一個網上寫的不錯的程式碼:
BaseServlet
2、springmvc重複的輪子:
springmvc的出現把這上面的程式碼多了許多層的封裝使得能夠處理更多的情況,例如:國際化,引數的格式化、引數的校驗……
三、DispatcherServlet:
1.DispatcherServlet的初始化圖
下面開始進去正題,DispatcherServlet的類圖結構:關於ApplicationContextAware這一塊的咱先不畫,暫時也不研究,後面再逐一攻破
看到這個類圖其實大家就應該在回憶回憶Servlet的類圖了(回憶一下核心的知識點),有很多的方法我都沒有標註,只是標註了一些比較核心的方法,在這裡我寫出一些常見的面試題大家回一下:
- servlet的生命週期
- jsp的九大隱式物件
- jsp的四大作用域
- servlet是單例的嗎?是執行緒安全的嗎?
- forwar和redirect的區別
- session和cookie的區別以及常見api
- 攔截器、監聽器(種類、作用、應用場景)
下面我們通過一個時序圖來畫一下DispatcherServlet初始化所幹的事兒(由於用的是老版的visio所以圖畫的有點爛,不過能看懂就好)寫部落格畫圖能力的確提升了不少。
我用紅色標註的地方請大家注意,這個是ioc的初始化入口,spring最為基礎核心的部分就是ioc,這個體系我也沒有弄的太熟悉,所以等我把腦圖整理好,然後在寫一個系統化的博文出來。
上面的兩幅圖大概可以把DispatcherServlet的問題出明白,其實spring的東西以及體系是非常的大的,想要一篇博文把所有的東西出明白是不現實的,所以這一系列的博文會挑一些重點的來說。
2.其它可關注的點:
- spring的事件體系(這個體系到現在我沒理清楚,所以只能見一個折騰一個),這個就是當springmvc收到refresh event的時候重新 執行onRefresh方法重新初始化springmvc的九大元件
- 在HttpServletBean中出現的BeanWrapper的作用,有興趣可以多研究研究
大多出現Wrapper的都是和Bean的反射內省是相關的,Mybatis也是同樣的套路
這個是目前整理的spring原始碼解析的相關的思維導圖,以及java併發相關的思維導圖,這個裡面有大量的備註,體系太龐大所以截圖難以截清楚,如果需要可以加入群,群是初建的所以沒有什麼人,希望大家可以成為朋友一起交流。後面也會把這些思維導圖做一個輸出寫成部落格來豐富和系統化自己的知識體系。下期博文:springmvc請求應答流程、springmvc九大元件的講解。敬請期待!!!