Spring源碼分析(一):從哪裡開始看spring源碼

Tomcat 設計 Java WebApp XML 服務端開發 2019-06-09

概述

  • 對於大多數第一次看spring源碼的人來說,都會感覺不知從哪開始看起,因為spring項目源碼由多個子項目組成,如spring-beans,spring-context,spring-core,spring-aop,spring-web,spring-webmvc等,整個項目結構如圖:
Spring源碼分析(一):從哪裡開始看spring源碼

  • 可能有人會覺得,既然spring是一個IOC容器或者說是一個bean的容器,那麼應該從spring-beans看起,先了解spring是如何從xml文件配置獲取需要創建的bean的信息,但是這裡有個問題就是雖然知道怎麼遍歷初始化,但是不知道哪裡用到或者說哪裡讓這些初始化開始,而且像BeanFactory,FactoryBean,Environment,PropertySource等接口還是比較抽象的,比較難看懂,所以很容易讓人感覺枯燥,然後就放棄了。
  • 我們可以換個思路,從能接觸到的角度開始,即我們通常會使用spring-mvc來進行web開發,如@Controller,@RequestMapping都是再熟悉不過的了。如果搭過spring-mvc項目都知道,通常需要在web.xml文件中,配置一個ContextLoaderListener,contextConfigLocation,DispatcherServlet,可能很多人都是從網上copy了一份配置過來或者知道contextConfigLocation是指定spring配置文件的位置,DispatcherServlet是接收所有請求的前端控制器,需要指定攔截路由:“/”,從而攔截所有URL中帶“/”的請求,但是在spring源碼中是怎麼使用這些組件的呢?以及怎麼配置了一個@Controller,@RequestMapping中指定了一個url,就可以訪問了呢?還有就是通常我們的web項目都會部署在web容器,如tomcat當中,那麼tomcat和spring有啥關係呢?所以我們可以帶著這些問題去查看spring源碼找到答案。
  • 所以我推薦是從spring-mvc開始看spring源碼,因為這個是我們使用得比較多,比較容易理解的一個模塊,然後一層一層往上剝,找到與spring-context,spring-beans,spring-aop等的關係。如果真的對JavaWeb開發,Java EE很感興趣,或者更容易讀懂spring的源碼,可以先看servlet規範和Tomcat的設計與Tomcat的請求處理工作流。我目前也在結合這兩個方面看,也可以看下我的Tomcat源碼分析系列。

Servlet規範

  • 在servlet的規範當中,servlet容器或者叫web容器,如tomcat,中運行的每個應用都由一個ServletContext表示,在web容器中可以包含多個ServletContext,即可以有多個web應用在web容器中運行。如在tomcat的webapp目錄下,每個war包都對應一個web應用,tomcat啟動時會解壓war包,並啟動相關的應用。
  • 在web容器啟動的時候,會初始化web應用,即創建ServletContext對象,加載解析web.xml文件,獲取該應用的Filters,Listener,Servlet等組件的配置並創建對象實例,作為ServletContext的屬性,保存在ServletContext當中。之後web容器接收到客戶端請求時,則會根據請求信息,匹配到處理這個請求的Servlet,同時在交給servlet處理之前,會先使用應用配置的Filters對這個請求先進行過濾,最後才交給servlet處理。
  • 瞭解web容器啟動,之後接受客戶端請求這些知識有啥用處呢?這裡我們需要回過頭來看我們的spring項目。我們在日常開發中,直接接觸的是spring相關的組件,然後打成war包,放到web容器中,如拷貝到tomcat的webapp目錄,並不會直接和web容器打交道。經過以上的分析,其實一個spring項目就是對應web容器裡的一個ServletContext,所以在ServletContext對象的創建和初始化的時候,就需要一種機制來觸發spring相關組件的創建和初始化,如包含@Controller和@RequestMapping註解的類和方法,這樣才能處理請求。

Listener監聽器機制:ContextLoaderListener

  • servlet規範當中,使用了Listener監聽器機制來進行web容器相關組件的生命週期管理以及Event事件監聽器來實現組件之間的交互。
  • 其中一個重要的生命週期監聽器是ServletContextListener。web容器在創建和初始化ServletContext的時候,會產生一個ServletContextEvent事件,其中ServletContextEvent包含該ServletContext的引用。然後交給在web.xml中配置的,註冊到這個ServletContext的監聽器ServletContextListener。ServletContextListener在其contextInitialized方法中定義處理邏輯,接口定義如下:

public interface ServletContextListener extends EventListener {
// 初始化servletcontext
public void contextInitialized(ServletContextEvent sce);

// 銷燬servletcontext
public void contextDestroyed(ServletContextEvent sce);
}
  • 從contextInitialized的註釋可知:通知所有的ServletContextListeners,當前的web應用正在啟動,而且這些ServletContextListeners是在Filters和Servlets創建之前接收到通知的。所以在這個時候,web應用還不能接收請求,故可以在這裡完成底層處理請求的組件的加載,這樣等之後接收請求的Filters和Servlets創建時,則可以使用這些創建好的組件了。spring相關的bean就是這裡所說的底層處理請求的組件,如數據庫連接池,數據庫事務管理器等。
  • ContextLoaderListener:spring-web包的ContextLoaderListener就是一個ServletContextListener的實現類。ContextLoaderListener主要用來獲取spring項目的整體配置信息,並創建對應的WebApplicationContext來保存bean的信息,以及創建這些bean的對象實例。默認去WEB-INF下加載applicationContext.xml配置,如果applicationContext.xml放在其他位置,或者使用其他不同的名稱,或者使用多個xml文件,則與指定contextConfigLocation。具體spring源碼的實現過程後續文章詳細分析。
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 修改配置文件路徑 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext.xml</param-value>
</context-param>

DispatcherServlet:前端控制器

  • 在web容器中,web.xml中的加載順序:context-param -> listener -> filter -> servlet。其中ContextLoaderListener是屬於listener階段。我們通常需要在項目的web.xml中配置一個DispatcherServlet,並配置攔截包含“/”路徑的請求,即攔截所有請求。這樣在web容器啟動應用時,在servlet階段會創建這個servlet,由Servlet規範中servlet的生命週期方法可知:
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
  • web容器在創建這個servlet的時候,會調用其init方法,故可以在DispatcherServlet的init方法中定義初始化邏輯,核心實現了創建DispatcherServlet自身的一個WebApplicationContext,注意在spring中每個servlet可以包含一個獨立的WebApplicationContext來維護自身的組件,而上面通過ContextLoaderListener創建的WebApplicationContext為共有的,通常也是最頂層,即root WebApplicationContext,servlet的WebApplicationContext可以通過setParent方法設值到自身的一個屬性。DispatcherServlet默認是加載WEB-INF下面的“servletName”-servlet.xml,來獲取配置信息的,也可以與ContextLoaderListener一樣通過contextLoaderConfig來指定位置。DispatcherServlet具體的源碼設計在之後文章詳細分析。

總結

  • 從上面的分析,可知spring相關配置解析和組件創建其實是在web容器中,啟動一個web應用的時候,即在其ServletContext組件創建的時候,首先解析web.xml獲取該應用配置的listeners列表和servlet列表,然後保存在自身的一個屬性中,然後通過分發生命週期事件ServletContextEvent給這些listeners,從而在listeners感知到應用在啟動了,然後自定義自身的處理邏輯,如spring的ContextLoaderListener就是解析spring的配置文件並創建相關的bean,這樣其實也是實現了一種代碼的解耦;其次是創建配置的servlet列表,調用servlet的init方法,這樣servlet可以自定義初始化邏輯,DispatcherServlet就是其中一個servlet。
  • 所以在ContextLoaderListener和DispatcherServlet的創建時,都會進行WebApplicationContext的創建,這裡其實就是IOC容器的創建了,即會交給spring-context,spring-beans包相關的類進行處理了,故可以從這裡作為一個入口,一層一層地剝spring的源碼了。

相關推薦

推薦中...