當5G來臨,當211高校已經開啓人工智能課程,當甲骨文大批量裁人,你們的心是否像我同樣爲之一顫呢?當科技不斷髮展,技術迅速迭代,程序員愈發年輕化的今天,而做爲咱們已經步入中年的程序員來講路在何方?當咱們逐漸老去,咱們不能期望企業家的憐憫,當大批年輕化的程序員涌入互聯網大潮時,他們的思惟,他們的體能,甚至他們的能力都遠超於咱們,咱們又該何去何從?職場不相信眼淚,更不會同情,惟有修煉內功,修煉職場硬實力,固然若是硬實力不行,也只能來點軟的了......程序員
做爲程序員不懂高併發、JVM優化、系統內核、大數據、框架源碼... ...,成天寫CRUD,也許很快就會被時代所淘汰。spring
下面讓我帶你抽絲剝繭spring源碼。會從spring ioc容器核心結構、spring ioc注入執行流程的脈絡講起,包括什麼是BeanFactory和FactoryBean;什麼是BeanDefinition及其有哪些重要的實現類;什麼是BeanPostProcessor、BeanFactoryPostProcessor、ImportSelector、ImportBeanDefinitionRegistrar,以及如何應用他們來實現對spring的擴展等等,逐步深刻細節,固然spring源碼很是龐大難懂,本文是從spring ioc講起,重點是梳理spring ioc注入的脈絡,以後的博文會一點點抽絲剝繭的對spring內部處理細節作逐步分析講解,包括Spring aop、spring Tx等等,來逐步解密spring。mybatis
1、帶您初識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中的哪一個類應用了,你們本身想吧。post
二、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核心類。
2、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容器目前的狀況:
接下來就要處理咱們須要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工廠中。
上面主要說了spring ioc容器啓動過程的前兩個方法,下面咱們來看下spring ioc容器啓動的最核心最重要的方法refresh()。
下面就讓咱們看下refresh()方法都作了什麼?
上面是refresh()代碼的主體部分,spring ioc容器初始化都會在這裏完成,其內部代碼很是複雜,這裏咱們就先了解下其處理脈絡,看下每一個方法都作了什麼,在以後的博文中會詳細講解其處理細節。
2.4.1 prepareRefresh()
prepareRefresh():這個方法比較簡單,主要是設置了啓動時間、啓動標識等操做,不是重點,先不用關注。
2.4.2 obtainFreshBeanFactory()
這個方法顧名思義,就是從新獲取beanFactory實例,這個方法沒有什麼可說的,就是獲取了以前咱們初始化的beanFactory實例DefaultListableBeanFactory。
GenericApplicationContext:
2.4.3 prepareBeanFactory()
準備bean工廠初始化環境。主要是注入spring內部bean、註冊Aware相關接口。
2.4.4 postProcessBeanFactory()
空方法。
2.4.5 invokeBeanFactoryPostProcessors()
這是比較重要的方法之一。其內部處理比較複雜,固然其處理方式是很是值得學習的,想要知道這個方法作了什麼嗎?下節我將帶你們一塊兒分析本方法和後續的處理方法的脈絡部分。時間不早了,先洗洗睡了。