文本已收錄至個人GitHub倉庫,歡迎Star:github.com/bin39232820…
種一棵樹最好的時間是十年前,其次是如今
我知道不少人不玩qq了,可是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵你們在技術的路上寫博客java
一樣這篇文章也是直接copy的,由於子路老師已經寫得很好了。我這邊至關於從新學習一遍,加深印象git
若是想系統的學習spring源碼那麼第一個須要搞明白的知識即是spring當中的BeanDefinition——spring bean的建模對象;程序員
筆者特別強調,beanDefintion的比較枯燥和晦澀難懂,可是很是很是重要,我打算寫三篇來把beanDefintion知識講完;若是你想精讀spring源碼,請你必定細讀三篇beanDefintion的知識,他是spring framework當中的基石;github
那麼什麼是spring bean的建模對象呢?一言概之就是把一個bean實例化出來的模型對象?有人會問把一個bean實例化出來有Class就好了啊,Class也就是咱們一般說的類對象,就是一個普通對象的建模對象,那麼爲何spring不能用Class來創建bean呢?很簡單,由於Class沒法完成bean的抽象,好比bean的做用域,bean的注入模型,bean是不是懶加載等等信息,Class是沒法抽象出來的,故而須要一個BeanDefinition類來抽象這些信息,以便於spring可以完美的實例化一個bean(關於什麼是bean什麼是對象,請參考我寫的第一篇循環引用筆者有解釋)spring
上述文字能夠簡單理解spring當中的BeanDefinition就是java當中的Class Class能夠用來描述一個類的屬性和方法等等其餘信息 BeanDefintion能夠描述springbean當中的scope、lazy,以及屬性和方法等等其餘信息api
對上圖的文字說明:假設磁盤上有N個.java文件,首先咱們把這些java文件編譯成class文件,繼而java虛擬機啓動會把這些class文件load到內存,當遇到new關鍵字的時候會根據類的模板信息實例化這個對象也就是在堆上面分配內存bash
下面我會對這幅圖作大篇幅的說明,在springbean實例化簡圖當中我一共標記了⑤步;逐一來講明吧mybatis
前提:假設在你的項目或者磁盤上有X和Y兩個類,X是被加了spring註解的,Y沒有加spring的註解;也就是正常狀況下當spring容器啓動以後經過getBean(X)能正常返回X的bean,可是若是getBean(Y)則會出異常,由於Y不能被spring容器掃描到不能被正常實例化;app
①[^1]當spring容器啓動的時候會去調用ConfigurationClassPostProcessor這個bean工廠的後置處理器完成掃描,關於什麼是bean工廠的後置處理器下文再來詳細解釋;spring完成掃描的具體源碼放到後續的文章中再來講。閱讀本文讀者只需知道掃描具體幹了什麼事情便可;其實所謂的spring掃描就是把類的信息讀取到,可是讀取到類的信息存放到哪裏呢?好比類的類型(class),好比類的名字,類的構造方法。可能有讀者會有疑問這些信息不須要存啊,直接存在class對象裏面不就能夠?好比當spring掃描到X的時候Class clazzx = X.class;那麼這個classx裏面就已經具有的前面說的那些信息了,確實如此,可是spring實例化一個bean不只僅只須要這些信息,還有我上文說到的scope,lazy,dependsOn等等信息須要存儲,因此spring設計了一個BeanDefintion的類用來存儲這些信息。框架
故而當spring讀取到類的信息以後②[2]會實例化一個BeanDefinition的對象,繼而調用這個對象的各類set方法存儲信息;每掃描到一個符合規則的類,spring都會實例化一個BeanDefinition對象,而後把根據類的類名生成一個bean的名字(好比一個類IndexService,spring會根據類名IndexService生成一個bean的名字indexService
,spring內部有一套默認的名字生成規則,可是程序員能夠提供本身的名字生成器覆蓋spring內置的,這個後面更新),
③
[3]繼而spring會把這個beanDefinition對象和生成的beanName放到一個map當中,key=beanName,value=beanDefinition對象;至此上圖的第①②③步完成。
這裏須要說明的是spring啓動的時候會作不少工做,不只僅是完成掃描,在掃描以前spring還幹了其餘大量事情;好比實例化beanFacctory、好比實例化類掃描器等等,這裏不討論,在之後的文章再來討論
用一段代碼和結果來證實上面的理論
Appconfig.java
@ComponentScan("com.luban.beanDefinition")
@Configuration
public class Appconfig {
}
X.java
@Component
public class X {
public X(){
System.out.println("X Constructor");
}
}
Y.java
public class Y {
}
Test.java
public class Test{
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(Appconfig.class);
ac.refresh();
}
}
複製代碼
上述代碼裏面有X和Y兩個類(下面有筆者運行這些代碼的結果截圖),X被註解了,Y沒註解,而且在X當中有個構造方法一旦X被實例化便會打印"X Constructor";並且在main方法的最開始打印了"start"按照上面筆者的理論spring首先會掃描X繼而把X解析稱爲一個beanDefinition對象放到map;筆者在spring的源碼org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors方法上打了一個斷點(invokeBeanFactoryPostProcessors方法裏面就完成了上述所說的掃描解析功能),當應用程序啓動的時候首先會打印start,繼而啓動spring容器,而後調用invokeBeanFactoryPostProcessors方法,在沒有執行該方法以前查看beanDefintionMap當中並無key爲"x"的元素,說明X並無被掃描,而後繼續執行,當執行完invokeBeanFactoryPostProcessors方法時候再次查看beanDefintionMap能夠看到map當中多了一個key爲"x"的元素,其對應的value就是一個beanDefintion的對象,最後查看後臺發現尚未打印"X Constructor"這說明這個時候X並無被實例化,這個例子說明spring是先把類掃描出來解析稱爲一個beanDefintion對象,而後put到beanDefintionMap後面纔會去實例化X,至於這個beanDefintionMap後面的文章我會詳細講解,本文讀者只需知道他是一個專門來存放beanDefinition的集合便可
④當spring把類所對應的beanDefintion對象存到map以後,spring會調用程序員提供的bean工廠後置處理器。什麼叫bean工廠後置處理器?在spring的代碼級別是用一個接口來表示BeanFactoryPostProcessor,只要實現這個接口即是一個bean工廠後置處理器了,BeanFactoryPostProcessor的詳細源碼解析後面文章再來分析,這裏先說一下他的基本做用。BeanFactoryPostProcessor接口在spring內部也有實現,好比第①步當中完成掃描功能的類ConfigurationClassPostProcessor即是一個spring本身實現的bean工廠後置處理器,這個類筆者認爲是閱讀spring源碼當中最重要的類,沒有之一;他完成的功能太多了,之後咱們一一分析,先看一下這個類的類結構圖。
ConfigurationClassPostProcessor實現了不少接口,和本文有關的只需關注兩個接口BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor;可是因爲BeanDefinitionRegistryPostProcessor是繼承了BeanFactoryPostProcessor因此讀者也能夠理解這是一個接口,可是筆者更加建議你理解成兩個接口比較合適,由於spring完成上述①②③步的功能就是調用BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法完成的,到了第④步的時候spring是執行BeanFactoryPostProcessor的postProcessBeanFactory方法;這裏可能說的有點繞,大概意思spring完成①②③的功能是調用ConfigurationClassPostProcessor的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,到了第④步spring首先會調用ConfigurationClassPostProcessor的BeanFactoryPostProcessor的postProcessBeanFactory的方法,而後在調用程序員提供的BeanFactoryPostProcessor的postProcessBeanFactory方法,因此上圖當中第④步我畫的是紅色虛線,由於第④步可能沒有(若是程序員沒有提供本身的BeanFactoryPostProcessor;固然這裏必定得說明,即便程序員沒有提供本身擴展的BeanFactoryPostProcessor,spring也會執行內置的BeanFactoryPostProcessor也就是ConfigurationClassPostProcessor,因此上圖畫的並不標準,少了一步;即spring執行內置的BeanFactoryPostProcessor;
重點:咱們用本身的話總結一下BeanFactoryPostProcessor的執行時機(無論內置的仍是程序員提供)————Ⅰ:若是是直接實現BeanFactoryPostProcessor的類是在spring完成掃描類以後(所謂的掃描包括把類變成beanDefinition而後put到map之中),在實例化bean(第⑤步)以前執行;Ⅱ若是是實現BeanDefinitionRegistryPostProcessor接口的類;誠然這種也叫bean工廠後置處理器他的執行時機是在執行直接實現BeanFactoryPostProcessor的類以前,和掃描(上面①②③步)是同期執行;假設你的程序擴展一個功能,須要在這個時期作某個功能則能夠實現這個接口;可是筆者至今沒有遇到這樣的需求,若是之後遇到或者看到再來補上;(說明一下當筆者發表這篇博客有一個讀者聯繫筆者說他看到了一個主流框架就是擴展了BeanDefinitionRegistryPostProcessor類,瞬間筆者如久旱遇甘霖,光棍遇寡婦;遂立馬請教;那位讀者說mybatis的最新代碼裏面即是擴展這個類來實現的,筆者記得之前mybatis是擴展沒有用到這個接口,後來筆者看了一下最新的mybatis源碼確實如那位讀者所言,在下一篇筆者分析主流框架如何擴展spring的時候更新)
這個地方讀者插一句,真的這個 Spring 的各類 PostProcess真的是強大,我在這稱他爲鉤子函數,Spring的設計強大之處的設計就是,他能作到最大限度的一個 高內聚,低耦合,而且作到面對修改關閉,面對拓展開放,正由於這些鉤子函數,他容許咱們常常去插手他的各類事情,你好比在初始化話過程當中,就不少地方容許咱們去幹預他的初始化,他就是作到了,若是你要干預我,就以你爲準,否則我就有一套自我運行的機制,簡直就是棒
那麼第④步當中提到的執行程序員提供的BeanFactoryPostProcessor到底有什麼意義呢?程序員提供BeanFactoryPostProcessor的場景在哪裏?有哪些主流框架這麼幹過呢?
首先回答第一個問題,意義在哪裏?能夠看一下這個接口的方法簽名
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException;
複製代碼
其實討論這個方法的意義就是討論BeanFactoryPostProcessor的做用或者說意義,參考這個方法的一句javadoc
Modify the application context’s internal bean factory after its standard initialization 在應用程序上下文的標準初始化以後修改它的內部bean工廠
再結合這個方法的執行時機和這段javadoc咱們能夠理解bean工廠的後置處理器(這裏只討論直接實現BeanFactoryPostProcessor的後置處理器,不包括實現BeanDefinitionRegistryPostProcessor的後置處理器)實際上是spring提供的一個擴展點(spring提供不少擴展點,學習spring源碼的一個很是重要的緣由就是要學會這些擴展點,以便對spring作二次開發或者寫出優雅的插件),可讓程序員干預bean工廠的初始化過程(重點會考);這句話最重要的幾個字是初始化過程,注意不是實例化過程 ;初始化和實例化有很大的區別的,特別是在讀spring源碼的時候必定要注意這兩個名詞;翻開spring源碼你會發現整個容器初始化過程就是spring各類後置處理器調用過程;而各類後置處理器當中大致分爲兩種;一種關於實例化的後置處理器一種是關於初始化的後置處理器,這裏不是筆者臆想出來的,若是讀者熟悉spring的後置處理器體系就能夠從spring的後置處理器命名看出來spring對初始化和實例化是有很是大的區分的。
說白了就是——beanFactory怎麼new出來的(實例化)BeanFactoryPostProcessor是干預不了的,可是beanFactory new出來以後各類屬性的填充或者修改(初始化)是能夠經過BeanFactoryPostProcessor來干預;能夠看到BeanFactoryPostProcessor裏惟一的方法postProcessBeanFactory中惟一的參數就是一個標準的beanFactory對象——ConfigurableListableBeanFactory;既然spring在調用postProcessBeanFactory方法的時候把已經實例化好的beanFactory對象傳過來了,那麼天然而然咱們能夠對這個beanFactory肆意妄爲了;
雖然肆意妄爲聽起來很酷,實則不少人會很迷茫;就至關於如今送給了你一輛奧迪A6(筆者的dream car!重點會考)告訴你能夠對這輛車肆意妄爲,可你若是隻是會按按喇叭,那就對不起贈送者的一番美意了;其實你根本不知道能夠在午夜開車這輛車去長沙的解放西路轉一圈,繼而會收貨不少意外的「愛情」;筆者舉這個例子就是想說當你拿到beanFactory對象的時候不能只會sout,那不叫肆意妄爲;咱們能夠幹不少事情,可是你必需要了解beanFactory的特性或者beanFactory的各類api,可是beanFactory這個對象太複雜了,這裏不適合展開討論,與本文相關的只要知道上述咱們講到的那個beanDefintionMap(存儲beanDefintion的集合)就定義在beanFactory當中;並且他也提供額api供程序員來操做這個map,好比能夠修改這個map當中的beanDefinition對象,也能夠添加一個beanDefinition對象到這個map當中;看一段代碼
@Component
public class TestBeanFactoryPostPorcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//轉換爲子類,由於父類沒有添加beanDefintion對象的api
DefaultListableBeanFactory defaultbf =
(DefaultListableBeanFactory) beanFactory;
//new一個Y的beanDefinition對象,方便測試動態添加
GenericBeanDefinition y= new GenericBeanDefinition();
y.setBeanClass(Y.class);
//添加一個beanDefinition對象,本來這個Y沒有被spring掃描到
defaultbf.registerBeanDefinition("y", y);
//獲得一個已經被掃描出來的beanDefintion對象x
//由於X原本就被掃描出來了,因此是直接從map中獲取
BeanDefinition x = defaultbf.getBeanDefinition("x");
//修改這個X的beanDefintion對象的class爲Z
//本來這個x表明的class爲X.class;如今爲Z.class
x.setBeanClassName("com.luban.beanDefinition.Z");
}
}
複製代碼
項目裏面有三個類X,Y,Z其中只有X加了@Component註解;也就是當代碼執行到上面那個方法的時候只掃描到了X;beanFactory裏的beanDefinitionMap當中也只有X所對應的beanDefinition對象;筆者首先new了一個Y所對應的beanDefinition對象而後調用registerBeanDefinition("y", y);把y對應的beanDefinition對象put到beanDefinitionMap,這是演示動態添加一個本身實例化的beanDefinition對象;繼而又調用getBeanDefinition("x")獲得一個已經存在的beanDefinition對象,而後調用x.setBeanClassName("Z");把x所對應的beanDefinition對象所對應的class改爲了Z,這是演示動態修改一個已經掃描完成的beanDefinition對象;
測試代碼以下: public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); ac.register(Appconfig.class); ac.refresh(); //正常打印 System.out.println(ac.getBean(Y.class)); //正常打印 System.out.println(ac.getBean(Z.class)); //異常打印 //雖然X加了註解,可是被偷樑換柱了,故而異常 System.out.println(ac.getBean(X.class)); }
附圖:
總結一下上面那副圖,spring實例化一個bean其實和你提供的那個類並無直接關係,而是和一個beanDefintion對象所對應的那個類有直接關係(正常狀況下一個beanDefinition對象會對應一個類,可是也有不正常的狀況);打個庸俗的比方比如讀者你喜歡一個女生小A;而小A喜歡筆者,可是你不能說你也喜歡筆者;並且筆者仍是鋼鐵直男不管如何也不被可能掰彎的;
看完這個結果你還敢輕視spring當中的建模對象beanDefintion的做用了嗎?可是BeanDefinition這個接口有太多的實現類,是一個比較複雜的體現,下一篇我慢慢把spring體現當的各類beanDefinition對象逐一介紹清楚;
**我在想若是一個只會Spring應用的程序員,哪天他得罪你了 ,你偷偷寫個配置類,讓他的Service 永遠也註冊不到Spring中去,我估計他會瘋,,哈哈固然我只是告訴你們,瞭解一下底層的代碼仍是頗有必要,並無讓你們這樣去作
那麼第④步當中提到的執行程序員提供的BeanFactoryPostProcessor到底有什麼意義呢?程序員提供BeanFactoryPostProcessor的場景在哪裏?有哪些主流框架這麼幹過呢? 第一個問題咱們大概回答了一下,程序員通常提供BeanFactoryPostProcessor是爲了對beanFactory作修改或者叫作干預他的初始化;能修改什麼?或者能干預什麼?這個問題比較龐大,至少你如今知道能夠修改beanFactory當中的beanDefinitionMap 至於剩下兩個問題,我之後更新吧
我copy這篇文章的緣由,我是真以爲講得好,對Spring源碼的理解確定是到了必定的深度,對於我這種初涉源碼的小白都能理解,因此想着分享給你們。而後我但是跟着debug了一遍哈哈。建議全部的讀者不要看了就算了,能夠看一遍,本身再debug一遍,之後有空再畫個流程圖,基本上就轉換成本身的東西了,一開始都是copy嘛。
好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是真粉。
創做不易,各位的支持和承認,就是我創做的最大動力,咱們下篇文章見
六脈神劍 | 文 【原創】若是本篇博客有任何錯誤,請批評指教,不勝感激 !