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

來源:互聯網
上載者:User

標籤:web應用   contex   line   家庭   分享圖片   ref   9.png   www   blog   

在閱讀本文前,最好先閱讀以下內容(當然,如果對 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 啟動和載入過程(源碼調試)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.