萬字博文,Spring系列之抽絲剝繭Spring源碼(一)

當5G來臨,當211高校已經開啓人工智能課程,當甲骨文大批量裁員,大家的心是否像我一樣爲之一顫呢?當科技不斷髮展,技術迅速迭代,程序員愈發年輕化的今天,而作爲我們已經步入中年的程序員來說路在何方?當我們逐漸老去,我們不能指望企業家的憐憫,當大批年輕化的程序員涌入互聯網大潮時,他們的思維,他們的體能,甚至他們的能力都遠超於我們,我們又該何去何從?職場不相信眼淚,更不會同情,唯有修煉內功,修煉職場硬實力,當然如果硬實力不行,也只能來點軟的了…

作爲程序員不懂高併發、JVM優化、系統內核、大數據、框架源碼… …,整天寫CRUD,也許很快就會被時代所淘汰。

下面讓我帶你抽絲剝繭spring源碼。會從spring ioc容器核心結構、spring ioc注入執行流程的脈絡講起,包括什麼是BeanFactory和FactoryBean;什麼是BeanDefinition及其有哪些重要的實現類;什麼是BeanPostProcessor、BeanFactoryPostProcessor、ImportSelector、ImportBeanDefinitionRegistrar,以及如何應用他們來實現對spring的擴展等等,逐步深入細節,當然spring源碼非常龐大難懂,本文是從spring ioc講起,重點是梳理spring ioc注入的脈絡,之後的博文會一點點抽絲剝繭的對spring內部處理細節做逐步分析講解,包括Spring aop、spring Tx等等,來逐步解密spring。

帶您初識spring容器模型

在這裏插入圖片描述
在學習spring ioc源碼之前,有必要先了解下其核心類的含義及作用。上面是一個粗略的spring bean工廠內部存儲,鑑於spring工廠比較龐大,上圖也只畫出了一部分比較重要的核心類,但是即便這樣這個圖片也已經看不清了,但是我會帶您一個個講解說明。

初始化spring容器測試代碼:

在這裏插入圖片描述在這裏插入圖片描述

  • 首先既然是spring ioc,它的最重要的作用就是管理bean,所以我們猜測肯定是會有一個bean工廠的,這個是很多框架都有的(如mybatis),上來就會來個工廠。這就是 BeanFactory,在spring中默認的實現類爲DefaultBeanFactory,DefaultBeanFactory作爲bean工廠的默認實現,主要提供了bean的註冊,bean的獲取取。當然bean的註冊和獲取的具體實現是由其子類AbstractBeanFactory和DefaultSingletonBeanRegistry類完成。
    其實在本文開始的時候我也提到了FactoryBean,那麼FactoryBean又是什麼的?它是一個spring提供的一個接口,其實大家目前可以簡單粗暴的理解爲是一個特殊的bean,在某些 情況下bean的實例化過程比較複雜,可以實現FactoryBean接口重寫getObject()方法來實現一個複雜bean的實例化,實現FactoryBean接口會在spring工廠中注入2個bean,實現 類本身會對應一個bean,beanName爲&+類名(首字母小寫),還有一個就是getObject()返回的bean,也就是我們自己自定義的bean,beanName爲返回的Object類的類名 (首字母小寫)。如我們比較熟悉的mybatis就是應用了spring的這個FactoryBean這個擴展類,具體mybatis中的哪個類應用了,大家自己想吧。

  • BeanDefinition:顧名思義,這個類是描述bean的接口。有兩個重要的實現類分別是RootBeanDefinition和AnnotatedGenericBeanDefinition。RootBeanDefinition是用來定 義Spring內部的bean,如ConfigurationClassPostProcessor;AnnotatedGenericBeanDefinition是用來定義我們自定義的bean,也就是我們注入spring容器,需要spring管理的bean。

  • 說完BeanDefinition,我們來認識下bean的元數據定義類。ClassMetadata:定義類的元數據,其中包括getClassName、isInterface、isAnnotation方法;MethodMetadata:定義方法的元數據;AnnotationMetadata:定義註解類元數據信息;

  • Spring初始化時bean是怎麼存儲的?答案是存儲在DefaultListableBeanFactory類的集合中,其中BeanDefinitionMap是存儲了beanName和BeanDefinition的映射,beanDefinitionNames存儲了beanName,見下圖:

在這裏插入圖片描述
在這裏插入圖片描述
但請注意此時bean還沒有實例化,實例化後,bean是存儲在DefaultSingletonBeanRegistry類中的singletonObjects中,這個我們在之後分析源碼的時候會詳細說。

  • AnnotatedBeanDefinitionReader:這個類是spring容器初始化的時候,在DefaultListableBeanFactory初始化之後就會實例化的一個類。它主要的作用是向spring bean工廠中註冊我們自定義的bean,但是這個類其實是委託了BeanDefinitionReaderUtils類去調用DefaultListableBeanFactory類去完成bean的註冊的,可見DefaultListableBeanFactory的重要性。

  • BeanDefinitionHolder:封裝了beanName和BeanDefinition,不用關注。

  • BeanFactoryPostProcessor:bean工廠的後置處理器,可以插手bean工廠初始化過程,實現這個接口會得到beanFactory實例,也就是可以操作bean工廠了。至於什麼時候會執行其實現類,那是後面博文會講解的內容。

  • BeanPostProcessor:bean的後置處理器。會插手bean實例化的過程,實現此接口可以操縱容器中正在初始化的bean,也就是說可以對bean屬性做修改。

  • BeanDefinitionRegistryPostProcessor:是個接口,實現了BeanFactoryPostProcessor接口,定義了postProcessorBeanDefinitionRegistry(registry)方法,也就是擴展了BeanFactoryPostProcessor接口,spring內部ConfigurationClassPostProcessor類實現了這個接口,去完成bean的解析。

  • 實例化AnnotatedBeanDefinitionReader時,同時spring會向容器中注入7個spring自帶的bean處理器。這些處理器在spring容器中都起着重要作用。這個也留待我們之後的博文中進行深入講解。

  • ClassPathBeanDefinitionScanner:主要用於包的掃描工作。後續博文中會詳細說。

    以上簡要帶大家認識了spring ioc注入過程中涉及到的一些比較重要的spring核心類。

Spring ioc初始化流程

在這裏插入圖片描述
下面我們就以註解方式初始化spring爲例講解下spring容器初始化過程。

Spring註解方式啓動:實例化AnnotationConfigApplicationContext,傳入需要掃描的配置類或需要注入的beanclass。接着我們就來進入主題,看下源碼入口:
在這裏插入圖片描述

實例化父類構造方法

如上圖,首先會執行其父類的構造方法,正如我這個方法上註釋的說明,主要實例化三個類,我們分別看下:

GenericApplicationContext:主要實例化了beanFactory,默認爲DefaultListableBeanFactory。
在這裏插入圖片描述

AbstractApplicationContext:主要實例化PathMatchingResourcePatternResolver。這個類我在第一部分沒有拿出來說明,其實可以理解它是資源文件解析器,提供瞭解析資源文件的功能。
在這裏插入圖片描述

DefaultResourceLoader:實例化類加載器,其實就是APPClassLoader的實例。
在這裏插入圖片描述

執行完父類構造方法後,我們看下spring容器中的變化,即向spring容器中實例化了:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

無參構造方法

父類構造方法執行結束,我們就來看下主體方法中的第一個方法this()。它會調用無參的構造方法:
在這裏插入圖片描述
通過上面對代碼的註解,你應該大致瞭解了這個構造方法做了什麼。下面就在來看下里面的實現。
在這裏插入圖片描述
這個類最終主要會調用AnnotationConfigUtils.registerAnnotationConfigProcessors()方法,也就是AnnotatedBeanDefinitionReader會委託AnnotationConfigUtils類做一些事情,那麼我們就看下registerAnnotationConfigProcessors()方法做了哪些事情。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
正如這段代碼中我註釋所寫的那樣,主要是向bean工廠中註冊spring內部自定義的bean。其中這些bean有的是實現了BeanPostProcessor、BeanFactoryPostProcessor接口的,這兩個接口我們在第一部分中做過說明。被註冊的6個bean在後續的操作中都有着各自的作用,這些類會在後續的博文中做詳細的介紹。

這裏每個bean都會封裝成RootBeanDefinition(這個我們在第一部分也做過說明,RootBeanDefinition是封裝spring內部自定義的bean的),最終會調用registry.registerBeanDefinition(beanName, definition)方法向容器中註冊bean。Registry其實就是DefaultListableBeanFactory的實例,那麼也就是調用DefaultListableBeanFactory類的registerBeanDefinition()方法:
在這裏插入圖片描述
在這裏插入圖片描述
上面是當前會執行的代碼部分,也就是會將那6個bean都註冊到beanDefinitionMap中。這也就是實例化AnnotatedBeanDefinitionReader類主要做的事情。

我們在來看下現在spring容器中的變化,其實就是bean工廠中註冊了7個spring自定義的bean:
在這裏插入圖片描述
在這裏插入圖片描述

上面說了構造函數中的this.reader = new AnnotatedBeanDefinitionReader(this)方法,接下來說下構造函數中ClassPathBeanDefinitionScanner實例化的過程。
在這裏插入圖片描述
其實這個實例化的過程比較簡單,除了實例化ClassPathBeanDefinitionScanner類之外,主要就是註冊了幾個註解:
在這裏插入圖片描述
暫時可以先不用管這部分,不重要。
此時就完成了spring容器初始化的第一步,再讓我們來看下spring容器目前的情況:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

register(annotatedClasses)註冊自定義bean

接下來就要處理我們需要spring容器來管理的beanclass了。其實就是調用AnnotatedBeanDefinitionReader的register方法,其中參數AnnotatedClasses是我們傳入的需要注入的bean,可以是一個帶有@ComponentScan註解的配置類,也可以是一個bean集合,也可以是一個單個bean,所以此處做了循環操作處理。
在這裏插入圖片描述
最終會調用doRegisterBean()方法:

這個方法首先會調用shouldSkip()方法對beanclass做校驗,具體校驗邏輯見我對代碼的註解說明:
在這裏插入圖片描述
接着會調用AnnotationConfigUtils.processCommonDefinitionAnnotations()方法,在這個方法中會給這個bean實例賦予屬性,主要是通過這個beanclass上的註解來賦值,會看是否的@Lazy的、@Primary的值、@DependsOn的值、@Role的值、@Description的值,beanclass上存在這些註解就會取值並賦給bean實例。具體實現我們在之後的博文中會詳細分析。
在這裏插入圖片描述
下面是這個方法中最重要的部分,也就是最終的注入。這個如下面第二個圖所示,它最終也會調用registry.registerBeanDefinition()方法,也就是DefaultListableBeanFactory類的registerBeanDefinition()方法,最終將annotationClass注入到beanDefinitionMap和BeanDefinitionNames中。這個方法上面有說過,這裏就不在說明了,這個方法中具體的細節會在之後的博文中詳細分析。
在這裏插入圖片描述
在這裏插入圖片描述
至此我們的register()方法就處理脈絡就說完了,我們在來看下此時的bean工廠的變化:
在這裏插入圖片描述
在這裏插入圖片描述
其實就是如我上面說的那樣將annotationClass(App.class)注入到了我們的bean工廠中。

refresh()

上面主要說了spring ioc容器啓動過程的前兩個方法,下面我們來看下spring ioc容器啓動的最核心最重要的方法refresh()。

下面就讓我們看下refresh()方法都做了什麼?
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
上面是refresh()代碼的主體部分,spring ioc容器初始化都會在這裏完成,其內部代碼非常複雜,這裏我們就先了解下其處理脈絡,看下每個方法都做了什麼,在之後的博文中會詳細講解其處理細節。

prepareRefresh()

prepareRefresh():這個方法比較簡單,主要是設置了啓動時間、啓動標識等操作,不是重點,先不用關注。

obtainFreshBeanFactory()

這個方法顧名思義,就是重新獲取beanFactory實例,這個方法沒有什麼可說的,就是獲取了之前我們初始化的beanFactory實例DefaultListableBeanFactory。
GenericApplicationContext:
在這裏插入圖片描述

prepareBeanFactory()

準備bean工廠初始化環境。主要是注入spring內部bean、註冊Aware相關接口。

postProcessBeanFactory()

空方法。

invokeBeanFactoryPostProcessors()

這是比較重要的方法之一。其內部處理比較複雜,當然其處理方式是非常值得學習的,想要知道這個方法做了什麼嗎?下節我將帶大家一起分析本方法和後續的處理方法的脈絡部分。

最後

Spring的設計思想及源碼不是一朝一夕能弄透的,需要長期的積累和思辨。我已開啓Spring系列源碼分析的教程,有興趣的小夥伴請關注我。如果本文對您有價值,不要忘了點贊、留言。

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