在閱讀本文前,最好先閱讀以下內容(當然,如果對 Servlet 已經有所瞭解,則可跳過): http://www.cnblogs.com/cyhbyw/p/8682078.html http://www.cnblogs.com/cyhbyw/p/8682307.html http://www.cnb ...
在閱讀本文前,最好先閱讀以下內容(當然,如果對 Servlet 已經有所瞭解,則可跳過):
http://www.cnblogs.com/cyhbyw/p/8682078.html
http://www.cnblogs.com/cyhbyw/p/8682307.html
http://www.cnblogs.com/cyhbyw/p/8682632.html
============分隔線==========================
在使用 SpringMVC 進行 Web 開發時,通常在 web.xml 中配置的 Servlet 都是 org.springframework.web.servlet.DispatcherServlet,那這個 DispatcherServlet 又是如何被 Tomcat 容器(或者其它容器)啟動並載入進來的呢?
帶著這個問題,寫了個簡單Demo進行源碼調試。
Demo代碼地址:https://github.com/cyhbyw/springMVC_atguigu_TongGang
Demo代碼工程:springMVC_DebugSourceCode
首先從靜態代碼的角度,可以看到 DispatcherServlet 類的承繼結構如下圖所示
- HttpServlet 及以上部分是 Servlet 標準中提供的介面及類
- DispatcherServlet、FrameworkServlet、HttpServletBean 三者是 SpringMVC 提供的類,且後者依次分別是前者的父類
現在開始源碼調試:
首先調用了 DispatcherServlet 的構造函數,並且從堆棧信息中可以看出,這是由 Tomcat 調用的
接下來當然是調用父類 FrameworkServlet 的構造函數
構造函數完成後,調用 Servlet 生命周期的 init() 方法;
提示,此處是 HttpServletBean 中的 init() 方法重寫了GenericServlet中的 init() 方法;
這就是之前說的,建議重寫這個空的 init() 方法而不建議重寫那個 init(ServletConfig config) 方法,看來 SpringMVC 也確實是這樣做的;
接下來代碼走到 Line136行,初始化容器Bean
接下來代碼走到 Line493 行,初始化Web應用上下文
接下來代碼走到 Line552 行,創建Web應用上下文
獲取到需要創建的Bean的Class
直接調用 getContextClass() 方法
而它內置的 contextClass 其實就是 XmlWebApplicationContext
XmlWebApplicationContext 的繼承結構如下圖所示,不用說,肯定也是 ApplicationContext 家庭中的成員
Line627 行就實例化了 XmlWebApplicationContext
同時,代碼會走到 Line633 行,配置並刷新Web應用上下文
Line655 添加了一個應用監聽器;(重要,後面會取出來用到)
註意,這裡(SourceFilteringListener類中)方法入參處的 ApplicationListener delegate = FrameworkServlet$ContextRefreshListener,且 SourceFilteringListener 類成員變數中的 GenericApplicationListener delegate = GenericApplicationListenerAdapter;同時方法入參中的 delegate 會被 GenericApplicationListenerAdapter 包裝後賦值給成員變數的 delegate(有點繞,所以用了三種顏色以示區分)
可以這樣來記憶或理解:
一、對於 SourceFilteringListener 來說,其成員變數 delegate 的類型是 GenericApplicationListenerAdapter
二、對於 GenericApplicationListenerAdapter 來說,它也有個叫做 delegate 的成員變數,且這個 delegate 的類型是 FrameworkServlet$ContextRefreshListener
(雖然這兩個同名叫做 delegate 的成員變數有點繞,但它們比較重要,後面會用到)
SourceFilteringListener 構造完成後,回到上一層方法調用處;
接下來,代碼走到 Line667 行進行刷新
這個 refresh() 方法是 Spring 中非常重要的一個方法,會調用多個方法執行多個動作,包括初始化BeanFactory、容器後處理器處理、初始化MessageSource、註冊監聽器等動作;
refresh() 方法非常重要!!!
refresh() 方法非常重要!!!
refresh() 方法非常重要!!!
這裡,暫時關心的是,它會讀取我們為 SpringMVC 所編寫的配置文件中的內容(如 annotation-driven & default-servlet-handler 等,這屬於上一篇文章的內容,具體可參見 這裡);
之後,它會調用 Line541 行的方法,完成刷新
經過幾個方法的調用,代碼走到 Line136 ,並且此處的 listener=SourceFilteringListener(通過 Line125 獲取到之前添加進來的Listener,且這個 listener=SourceFilteringListener)
然後調用 SourceFilteringListener 的 onApplicationEvent() 方法
繼續調用
繼續調用,註意當前類是 SourceFilteringListener,且這個 delegate=GenericApplicationListenerAdapter(就是之前設置進來的)
現在來到 GenericApplicationListenerAdapter 類中,註意此處的 delegate=FrameworkServlet$ContextRefreshListener(之前設置進來的),所以,實際上會調到 ContextRefreshListener 的 onApplicationEvent() 方法
進而調用到 FrameworkServlet 中內部類 ContextRefreshListener 的 onApplicationEvent() 方法,而它又是直接調用到 FrameworkServlet 的 onApplicationEvent() 方法
這個方法會調用到 onRefresh() 方法;而 FrameworkServlet 的 onRefresh() 方法預設實現為空(讓子類擴展)
自然,會調用到 DispatcherServlet 的 onRefresh() 方法上,而這個方法實際上調用了其它的一系列初始化方法,如 initHandlerMappings(context) & initHandlerAdapters(context),這樣在容器啟動的過程中,就已經初始化完成 HandlerMapping & HandlerAdapter
至此,DispatcherServlet 中與 Servlet 生命周期相關的 constructor() & init() 方法就已經基本完成了,接下來,就是對請求的響應,這會依次調用 Servlet 的 service() 方法,不屬於本文範疇啦~~~
簡單總結起來,Tomcat 容器啟動並載入 DispatcherServlet 時所做的主要工作如下:
- 調用 DispatcherServlet 的構造器(當然也會調用父類的構造器,不過構造器預設實現為空;這個動作很短,基本上可以忽略)
- 調用 GenericServlet 的 init() 方法,不過,這被 HttpServletBean 重寫了;同時,重寫的 HttpServletBean 的 init() 方法調用了 initServletBean() 方法;而 initServletBean() 方法會完成以下操作:
- 初始化(創建)一個 WebApplicationContext(實際上是 WebApplicationContext 類)
- 調用 AbstractApplicationContext 的 refresh() 方法,完成 BeanFactory創建、讀取 SpringMVC 配置文件內容、處理容器後處理器、初始化MessageResource、註冊監聽器等工作
- 通過上一步中讀取到的內容,初始化 HandlerMapping & HandlerAdapter 等工作
- ==上面3個步驟才是重要內容==
總的來說,DispatcherServlet 還是一個 Servlet,遵循 constructor() --> init() --> service() --> destroy() 方法的調用流程。只不過,它的這個 init() 方法確實比較複雜(這就是本文為什麼會這麼長的原因,不過,看到此處的讀者,恭喜,您已經看完啦!)。