Spring源碼分析(一):從哪里開始看spring源碼(系列文章基於Spring5.0)


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

 

 可能有人會覺得,既然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方法中定義處理邏輯,接口定義如下:
/**
 * Implementations of this interface receive notifications about changes to the
 * servlet context of the web application they are part of. To receive
 * notification events, the implementation class must be configured in the
 * deployment descriptor for the web application.
 *
 * @see ServletContextEvent
 * @since v 2.3
 */
public interface ServletContextListener extends EventListener {

    /**
     ** Notification that the web application initialization process is starting.
     * All ServletContextListeners are notified of context initialization before
     * any filter or servlet in the web application is initialized.
     * @param sce Information about the ServletContext that was initialized
     */
    public void contextInitialized(ServletContextEvent sce);

    /**
     ** Notification that the servlet context is about to be shut down. All
     * servlets and filters have been destroy()ed before any
     * ServletContextListeners are notified of context destruction.
     * @param sce Information about the ServletContext that was destroyed
     */
    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的源碼了。
 
獲取更多學習資料,可以加群:473984645或者掃描下方二維碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM