1. 程式人生 > >SpringMVC DispatcherServlet 啟動和加載過程(源碼調試)

SpringMVC DispatcherServlet 啟動和加載過程(源碼調試)

span del frame tex 內容 ext bubuko 進行 resource

在閱讀本文前,最好先閱讀以下內容(當然,如果對 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() 方法會完成以下操作:
  1. 初始化(創建)一個 WebApplicationContext(實際上是 WebApplicationContext 類)
  2. 調用 AbstractApplicationContext 的 refresh() 方法,完成 BeanFactory創建、讀取 SpringMVC 配置文件內容、處理容器後處理器、初始化MessageResource、註冊監聽器等工作
  3. 通過上一步中讀取到的內容,初始化 HandlerMapping & HandlerAdapter 等工作
  4. ==上面3個步驟才是重要內容==

總的來說,DispatcherServlet 還是一個 Servlet,遵循 constructor() --> init() --> service() --> destroy() 方法的調用流程。只不過,它的這個 init() 方法確實比較復雜(這就是本文為什麽會這麽長的原因,不過,看到此處的讀者,恭喜,您已經看完啦!)。

SpringMVC DispatcherServlet 啟動和加載過程(源碼調試)