Spring IoC源碼學習:總覽

目錄

Spring IoC源碼學習全系列

前言

本系列文章會介紹哪些東西

如何將 Spring 源碼導入到 IDE 中

關於學習源碼的好處

如何高效的學習 Spring 源碼

關於 IoC

IoC 構建過程

startupShutdownMonitor 屬性

prepareRefresh() 方法

obtainFreshBeanFactory() 方法

prepareBeanFactory(beanFactory) 方法

postProcessBeanFactory(beanFactory) 方法

invokeBeanFactoryPostProcessors(beanFactory) 方法

registerBeanPostProcessors(beanFactory) 方法

initMessageSource() 方法

initApplicationEventMulticaster() 方法

onRefresh() 方法

registerListeners() 方法

finishBeanFactoryInitialization(beanFactory) 方法

finishRefresh() 方法

總結


Spring IoC源碼學習全系列

小白也看得懂的 Spring IoC 核心流程介紹

Spring IoC源碼學習:總覽

Spring IoC源碼學習:ApplicationContext 刷新前的配置

Spring IoC源碼學習:obtainFreshBeanFactory詳解

Spring IoC源碼學習:parseDefaultElement詳解

Spring IoC源碼學習:parseCustomElement詳解

Spring IoC源碼學習:obtainFreshBeanFactory詳解

Spring IoC源碼學習:invokeBeanFactoryPostProcessors詳解

Spring IoC源碼學習:registerBeanPostProcessors詳解

Spring IoC源碼學習:finishBeanFactoryInitialization詳解

Spring IoC源碼學習:getBean詳解

Spring IoC源碼學習:createBean詳解(上)

Spring IoC源碼學習:createBean詳解(下)

Spring IoC源碼學習:@Autowire 詳解

Spring IoC源碼學習:finishRefresh 詳解

 

前言

Spring 作爲現在最優秀的框架之一,被廣泛的應用於 Java 項目中,但是大多數人都只知道如何使用,而不知其中的原理。對大多數人來說可能知道如何使用已經足夠了,但是對於想提升自己的人來說,學習 Spring 的源碼是一個不錯的選擇。在過去的幾個月裏,我利用空閒時間將 IoC 的相關源碼學習了一下,按照老習慣準備整理成筆記,以博文的形式記錄下來,供自己以後使用,也供其他網友參考。由於涉及到的代碼較多,因此會以一個系列的形式來呈現。

本系列文章只能作爲學習 Spring IoC 源碼中的一個參考,強烈不建議只看本系列的文章,而不自己去閱讀 Spring 的源碼。

 

注:本系列的博文基於版本:4.3.12.RELEASE,由於個人水平有限,因此文中難免有錯誤的地方,歡迎各位積極提出。

另外,在寫文章的同時,可能自己會有新的認識,因此本系列文章可能會有不斷的優化調整。

 

本系列文章會介紹哪些東西

由於 IoC 的代碼量太大,要把完整的代碼邏輯全部詳細講一遍比較費精力,因此本系列的文章會有取捨,以下內容會粗略帶過或者捨棄。

  1. 日常開發基本不會用到,並且對源碼全局的理解不會造成明顯的影響。
  2. 比較過時的用法,已經有更好的用法。
  3. 過於簡單的代碼(一眼就能看出意思)。

 

如何將 Spring 源碼導入到 IDE 中

1.以 IDEA 爲例,首先到Git上下載某個 RELEASE 版本的 Spring 源碼壓縮包:Spring 源碼地址,並解壓到本地磁盤中。

2.New -> Project from Existing Sources

3.找到源碼解壓的地址,選中並點擊 OK

4.選擇 Import project from external model,並選中 Gradle,然後點擊 Next,之後一路默認即可,如果有需要選擇的,選擇Yes。

 

5.導入之後需要一段時間來構建索引,最後完成之後如下。

 

關於學習源碼的好處

這邊再聊個小事情。17年初,我當時正在準備跳槽,有一次面試的時候,一個面試官問我:你覺得學習源碼有什麼好處?對於當時的我來說,學習源碼不過是爲了應付面試。到現在,我對這個問題有了一些新的想法,我目前感覺到的主要好處有:

  • 深入學習過源碼後,自己在使用的時候顯得更遊刃有餘。
  • 可以學習到一些優秀的代碼,無論是一些邏輯上的思路,還是僅僅是編碼風格。
  • 許多中間件都會基於 Spring 的擴展功能來實現,閱讀 Spring 源碼,能幫助你更好的閱讀中間件源碼。
  • 最後還是應付面試。

 

如何高效的學習 Spring 源碼

學習 Spring 源碼不同於學習 JDK 源碼,以前學習 JDK 源碼基本就純看源碼,但是 Spring 源碼太多了,你純看的話可能會無法很好的理解。以下幾個點,可以幫助你更好的學習 Spring 源碼。

  1. 保持耐心,Spring 源碼很多,想在短時間內就看透是很難的,需要保持持續性和耐心,一點點的去啃。
  2. 可以通過 Debug Spring 源碼的方式來幫助自己更好的理解。這邊可以新建一個只有基礎框架的空項目,然後根據自己的需要增加一些測試類。
  3. 代碼是最好的註釋。Spring 作爲最優秀的框架之一,其代碼質量必然不用擔心。因此,除了註釋外,我們還可以通過類名、方法名、變量名、打印的日誌來幫助我們理解。
  4. 由於邏輯比較複雜,因此在執行一個操作時可能會有一個很長的調用鏈,最終進行實際操作的方法,一般會以 do 爲方法的前綴。例如:對於創建 bean 實例來說,最終執行創建的方法爲:doCreateBean;對於加載 bean 定義,最終執行加載的方法爲:doLoadBeanDefinitions。
  5. 如果某些代碼看了很多遍還是無法很好的理解,可以到網上搜下別人的文章參考,如果沒有合適的參考,可以先放着,先往後看,也許等你把後面的流程看了,你在回過頭來看就理解了。因爲有很多地方是前後相關聯的,只看前面的是無法很好理解的。
  6. 最重要的一點:反覆看,反覆看。Spring 的源碼是一個大工程,想要一遍兩遍就喫透就基本不可能的,只有反覆的看、反覆的推敲才能逐漸的加深自己的理解。

 

關於 IoC

IoC 即 Inversion of Control,也就是控制反轉。在傳統的程序設計,我們直接在對象內部通過 new 來創建對象,是程序主動去創建依賴對象;而在 Spring 中有專門的一個容器來創建和管理這些對象,並將對象依賴的其他對象注入到該對象中,這個容器我們一般稱爲 IoC 容器。

所有的類的創建、銷燬都由 Spring 來控制,也就是說控制對象生存週期的不再是引用它的對象,而是 Spring。對於某個具體的對象而言,以前是它控制其他對象,現在是所有對象都被 Spring 控制,所以這叫控制反轉。

 

IoC 構建過程

Spring 初始化的入口在 ContextLoaderListener,如果你的項目用了 Spring,通常可以在 web.xml 中找到下面這行代碼。

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

ContextLoaderListener 是實現了 javax.servlet.ServletContextListener 接口的服務器端程序,隨 web 應用的啓動而啓動,只初始化一次,隨 web 應用的停止而銷燬。在web應用啓動的時候會調用 contextInitialized 方法,停止的時候會調用 contextDestroyed 方法。

從 ContextLoaderListener 類爲出發點,跟着下面的調用過程:

ContextLoaderListener.contextInitialized(ServletContextEvent event) -> 
    initWebApplicationContext(event.getServletContext());
ContextLoader.initWebApplicationContext(ServletContext servletContext) -> 		
    configureAndRefreshWebApplicationContext(cwac, servletContext);
ContextLoader.configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) ->
    wac.refresh(); 
AbstractApplicationContext.refresh()

我們最終來到了 AbstractApplicationContext.java 裏的 refresh() 方法,這個方法就是構建整個 IoC 容器過程的完整代碼,只要把這個方法裏的每一行代碼都瞭解了,基本上了解了大部分 Spring 的原理和功能。

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        } finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

startupShutdownMonitor 屬性

用於“刷新”和“銷燬”的同步監視器,說白了就是一個鎖。

 

prepareRefresh() 方法

爲刷新準備新的上下文環境,設置其啓動日期和活動標誌以及執行一些屬性的初始化。主要是一些準備工作,不是很重要的方法,可以先不關注。

 

obtainFreshBeanFactory() 方法

用於獲得一個新的 BeanFactory。

該方法會解析所有 Spring 配置文件(通常我們會放在 resources 目錄下),將所有 Spring 配置文件中的 bean 定義封裝成 BeanDefinition,加載到 BeanFactory 中。常見的,如果解析到<context:component-scan base-package="com.joonwhee.open" /> 註解時,會掃描 base-package 指定的目錄,將該目錄下使用指定註解(@Controller、@Service、@Component、@Repository)的 bean 定義也同樣封裝成 BeanDefinition,加載到 BeanFactory 中。
上面提到的“加載到 BeanFactory 中”的內容主要指的是以下3個緩存:

  • beanDefinitionNames緩存:所有被加載到 BeanFactory 中的 bean 的 beanName 集合。
  • beanDefinitionMap緩存:所有被加載到 BeanFactory 中的 bean 的 beanName 和 BeanDefinition 映射。
  • aliasMap緩存:所有被加載到 BeanFactory 中的 bean 的 beanName 和別名映射。


prepareBeanFactory(beanFactory) 方法

配置 beanFactory 的標準上下文特徵,例如上下文的 ClassLoader、後置處理器等。這個方法會註冊3個默認環境 bean:environment、systemProperties 和 systemEnvironment,註冊 2 個 bean 後置處理器:ApplicationContextAwareProcessor 和 ApplicationListenerDetector。


postProcessBeanFactory(beanFactory) 方法

允許子類對 BeanFactory 進行後續處理,默認實現爲空,留給子類實現。

 

invokeBeanFactoryPostProcessors(beanFactory) 方法

實例化和調用所有 BeanFactoryPostProcessor,包括其子類 BeanDefinitionRegistryPostProcessor。

BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 時對外暴露的擴展點,Spring IoC 容器允許 BeanFactoryPostProcessor 在容器實例化任何 bean 之前讀取 bean 的定義,並可以修改它。

BeanDefinitionRegistryPostProcessor 繼承自 BeanFactoryPostProcessor,比 BeanFactoryPostProcessor 具有更高的優先級,主要用來在常規的 BeanFactoryPostProcessor 激活之前註冊一些 bean 定義。特別是,你可以通過 BeanDefinitionRegistryPostProcessor 來註冊一些常規的 BeanFactoryPostProcessor,因爲此時所有常規的 BeanFactoryPostProcessor 都還沒開始被處理。 

注:這邊的 “常規 BeanFactoryPostProcessor” 主要用來跟 BeanDefinitionRegistryPostProcessor 區分。


registerBeanPostProcessors(beanFactory) 方法

註冊所有的 BeanPostProcessor,將所有實現了 BeanPostProcessor 接口的類加載到 BeanFactory 中。

BeanPostProcessor 接口是 Spring 初始化 bean 時對外暴露的擴展點,Spring IoC 容器允許 BeanPostProcessor 在容器初始化 bean 的前後,添加自己的邏輯處理。在這邊只是註冊到 BeanFactory 中,具體調用是在 bean 初始化的時候。

具體的:在所有 bean 實例化時,執行初始化方法前會調用所有 BeanPostProcessor 的 postProcessBeforeInitialization 方法,執行初始化方法後會調用所有 BeanPostProcessor 的 postProcessAfterInitialization 方法。

 

initMessageSource() 方法

初始化消息資源 MessageSource。

 

initApplicationEventMulticaster() 方法

初始化應用的事件廣播器 ApplicationEventMulticaster。

 

onRefresh() 方法

該方法爲模板方法,提供給子類擴展實現,可以重寫以添加特定於上下文的刷新工作,默認實現爲空。

 

registerListeners() 方法

註冊監聽器。


finishBeanFactoryInitialization(beanFactory) 方法

該方法會實例化所有剩餘的非懶加載單例 bean。除了一些內部的 bean、實現了 BeanFactoryPostProcessor 接口的 bean、實現了 BeanPostProcessor 接口的 bean,其他的非懶加載單例 bean 都會在這個方法中被實例化,並且 BeanPostProcessor 的觸發也是在這個方法中。

 

finishRefresh() 方法

完成此上下文的刷新,主要是推送上下文刷新完畢事件(ContextRefreshedEvent )到監聽器。

 

總結

上文標紅的方法爲構建 IoC 構成中最重要的4個方法,簡單的描述就是:

  • obtainFreshBeanFactory 創建一個新的 BeanFactory、讀取和解析 bean 定義。
  • invokeBeanFactoryPostProcessors 提供給開發者對 BeanFactory 進行擴展。
  • registerBeanPostProcessors 提供給開發者對 bean 進行擴展。
  • finishBeanFactoryInitialization 實例化剩餘的所有非懶加載單例 bean。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章