在上一篇文章裏面咱們討論了一個beanDefintion對象的重要性,爲了討論spring當中的beanDefinition對象咱們不得不牽扯出spring當中的bean工廠後置處理器也就是BeanFactoryPostProcessor這個類;繼而討論了BeanFactoryPostProcessor的大概執行時機(BeanFactoryPostProcessor的執行時機很重要並且spring內部作的稍微有點複雜,本文重點來討論spring內部怎麼保證這些執行時機得以嚴禁的執行,還有如何來擴展spring的bean工廠後置處理器);首先經過一張圖簡單的理解一下spring容器啓動的時候執行調用BeanFactoryPostProcessor後置處理器的大概的方法執行順序java
上圖大概分爲④步(這裏只是討論spring如何調用BeanFactoryPostProcessor,在調用以前到底執行了那些方法,上圖並非spring容器啓動的全部步驟) ①啓動main方法,在main方法裏面調用 ②AnnotationConfigApplicationContext的無參構造方法,而後②-1
在無參構造方法裏面首先實例化AnnotatedBeanDefinitionReader對象,實例化這個對象有兩個很是重要的意義。 一、這個對象實例化出來有什麼用(問題一) ; 二、實例化這個對象的過程當中作了哪些事情(問題二)程序員
全部的
問題X
,文章後面再解釋,這裏先把步驟搞清楚spring
繼而②-2spring又實例化了一個ClassPathBeanDefinitionScanner對象,這個對象顧名思義就是可以用來完成spring的掃描功能,可是這裏提一句——spring內部完成掃描功能並非用的這個對象,而是在掃描的時候又new了一個新的ClassPathBeanDefinitionScanner對象;換言之這裏spring new的對象咱們假設他爲a,可是spring在真正完成掃描的時候又new了一個b,可是是同一個類都是ClassPathBeanDefinitionScanner,可是爲何須要兩個? 三、直接用a不就能夠了嗎?若是spring內部完成掃描時候沒用a,那麼a被new出來在哪裏使用了?(問題三)到此爲止第②步完成; ③調用register(Appconfig.class);首先會把Appconfig類解析成爲一個beanDefintion對象(如何把一個類解析稱爲beanDefinition對象的?這裏其實涉及到②-1AnnotatedBeanDefinitionReader對象的意義,若是你明白了②-1這裏也就明白了),而後給解析出來的beanDefinition對象設置一些默認屬性,繼而put到beanDefintionMap當中;爲何須要put到beanDefintionMap呢?在上一篇咱們已經解釋過這個map就是單純用來存儲beanDefinition的,spring後面會遍歷這個map根據map當中的beanDefinition來實例化bean,若是Appconfig類的beanDefintion存在在map當中那麼他必然會被spring容器實例化稱爲一個bean?爲何Appconfig會須要實例化呢?由於Appconfig當中有不少加了@Bean的方法,這些方法須要被調用,故而須要實例化,可是Appconfig類的實例化很複雜比通常類實例化過程複雜不少,涉及到代理涉及到cglib等等,這個咱們後面文章解釋;這裏還須要解釋一個問題,爲何Appconfig類是經過register(Appconfig.class);手動put到map當中呢?爲何不是掃描出來的呢?(通常類都是經過掃描出來的),其實也很簡單,由於他沒法掃描本身,通常類是spring經過解析Appconfig上的@ComponentScan註解而後被掃描到,因此沒法掃描本身;接下來即是第 ④步,④-1到④-4之後分析,④-5
即是咱們上篇文章說的執行spring當中的bean工廠後置處理器,也是本文重點討論的;下圖是對上述文字的一個說明——spring容器啓動的執行順序 編程
接下來解釋上面文字裏的全部問題 問題一——AnnotatedBeanDefinitionReader這個對象實例化出來有什麼用? 先看一下這個類的javadoc,看看做者怎麼來解釋這個類的 api
Convenient adapter for programmatic registration of annotated bean classes. * This is an alternative to {@link ClassPathBeanDefinitionScanner}, applying * the same resolution of annotations but for explicitly registered classes onlybash
按照筆者蹩腳的英文水平個人理解是:這個類做用分爲如下兩個 一、可用於編程式動態註冊一個帶註解的bean,什麼意思呢?好比咱們有一個類A存在com.shadow包下面,而且是一個加註解的類。好比加了@Component,正常狀況下這個類A通常是被spring掃描出來的,可是有不正常狀況,好比spring並無掃描到com.shadow包,那麼類A就沒法被容器實例化。有人可能會問爲何沒有掃描到com.shadow包?掃描狀況下不會掃描到?其實很簡單,假設你的這個類是動態生成,在容器實例化的時候不存在那麼確定不存在,再或者這個包下面有N多類可是隻有一個類加了註解,那麼其實你不須要去掃描,只須要添加這一個加了註解的類便可,再或者一個類是你和第三方系統交互後獲得的。那麼這個時候咱們能夠把這個類經過AnnotatedBeanDefinitionReader的register(Class clazz)方法把一個帶註解的類註冊給spring(這裏的註冊其實就是上一篇文章中說的把一個類解析成BeanDefintion對象,而後把這個對象put到beanDefinitionMap當中),寫個例子來測試一下他的這個註冊bean的功能 app
二、能夠代替ClassPathBeanDefinitionScanner這個類,具有相同的註解解析功能, ClassPathBeanDefinitionScanner是spring完成掃描的核心類,這個我後面會分析;簡而言之,spring完成掃描主要是依靠ClassPathBeanDefinitionScanner這個類的對象,可是AnnotatedBeanDefinitionReader能夠替代他完成相同的註解解析,意思就是經過ClassPathBeanDefinitionScanner掃描出來的類A和經過AnnotatedBeanDefinitionReader顯示註冊的類A在spring內部會一套相同的解析規則;這點上面那個例子已經證實了。測試
那麼AnnotatedBeanDefinitionReader除了動態顯示註冊一些spring掃描不到的類以外還有什麼功能?在初始化spring容器的過程當中他主要乾了什麼事情呢?或者這麼說:假設程序中沒有須要動態顯示註冊的類他就沒用了嗎?再或者AnnotatedBeanDefinitionReader這個類的對象除了註冊一些本身的類還有什麼應用場景呢?——註冊咱們的配置類,對於不理解是什麼配置的讀者能夠理解所謂的配置類就是那個加了@Configuration和@ComponentScan的那個類,也就是Appconfig.java; 那麼問題來了,爲何配置類須要手動註冊呢?很簡單由於配置類沒法掃描出來,因此須要咱們手動註冊。爲何沒法掃描呢?好比spring完成掃描是須要解析Appconfig.java當中的@ComponentScan註解的值(通常是一個包名),獲得這個值以後去掃描這個值所表明的包下面的全部bean;簡單的說就是spring若是想要完成掃描必須先提供Appconfig.java,因此Appconfig.java要在一開始就手動註冊給spring,spring獲得Appconfig.class以後把他解析成BeanDefintion對象,繼而去獲取@ComponentScan的值而後纔開始掃描其餘bean;spa
總結:AnnotatedBeanDefinitionReader的做用一、主要是能夠動態、顯示的註冊一個bean;二、並且具有解析一個類的功能;和掃描解析一個類的功能相同;AnnotatedBeanDefinitionReader的應用場景一、能夠顯示、動態註冊一個程序員提供的bean;二、在初始化spring容器的過程當中他完成了對配置類的註冊和解析功能;翻譯
針對AnnotatedBeanDefinitionReader應用場景的第2點,我在囉嗦幾句,通常程序員在初始化spring容器的時候代碼有不少種寫法,但都是換湯不換藥的,我這裏舉2個栗子。 第一種寫法:
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext();
//動態註冊一個配置類
ac.register(Appconfig.class);
//調用refresh方法
//這裏不少資料都叫作刷新spring容器
//可是我以爲不合適,這是硬核翻譯,比較生硬
//筆者以爲理解爲初始化spring容器更加精準
//至於爲何之後慢慢更新再說
ac.refresh();
複製代碼
第二種寫法:
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(Appconfig.class);
複製代碼
這兩種寫法都初始化spring容器,代碼上的區別無非就是第一種寫法是調用AnnotationConfigApplicationContext();
的無參構造方法,第二種寫法是調用了AnnotationConfigApplicationContext(Class<?>... annotatedClasses)
有參構造方法;可是你若是翻閱源碼第二種寫法的內部也是首先調用無參構造方法的,繼而內部繼續調用register(Appconfig.class)方法,最後調用refresh();方法,和第一種寫法的區別是register和refresh是程序員調用的;筆者更推薦第一種寫法,由於第一種寫法能夠在初始化spring容器以前獲得AnnotationConfigApplicationContext的對象也就是代碼裏面的ac對象;用一張圖來講明一下這兩種寫法的異同
其實第一種方法和第二種方法均可以獲得ac對象,那麼爲何第一種寫法筆者推薦呢?首先第二種方法是在spring容器完成初始化以後的到的ac對象,容器已經初始化了,這個時候獲得這個對象能幹了事情少了不少;第一種方法在初始化以前獲得的,那麼能幹的事情可多了。 ①好比咱們能夠在容器初始化以前動態註冊一個本身的bean,就是上文提到的 AnnotatedBeanDefinitionReader的應用場景, ②再好比能夠利用ac對象來關閉或者開啓spring的循環依賴,在筆者的第一篇博客裏面也有提到, ③還好比程序員能夠在容器初始化以前註冊本身實例化的BeanDefinition對象。若是你像筆者同樣精通spring源碼你會發覺提早獲得這個ac對象能夠作的事情太多了,筆者這裏說的三個好比都必須在spring容器初始化以前作纔有意義,簡而言之就是須要在spring調用refresh方法之間作纔有意義;若是你不理解我打個比較污的比方; 假設有機會約到某喬姓網紅,無比興奮以後如今來分析原由和最終結果;原由是她願意出來和你約會,結果是約會完成最終她仍是要回家的。可是過程能夠很精彩呀!對比spring的源碼原由是你首先成功實例化了一個ac對象(ac成功實例化==你成功約會==new出來的ac對象==約出來的喬姑娘
)`,結果是調用ac.refresh()完成初始化spring容器,至關於喬姑娘回家==完成約會;
那麼你想一下第一種方式是你成功實例化一個ac對象,在完成初始化以前能夠肆意妄爲,除了對ac這個對象隨心所欲(調用ac的api)以外還能夠寫任何你本身想寫的代碼;至關於你成功約到喬姑娘,而後****************你能夠拿到她的身份證。可是第二種方式至關於你約到了喬姑娘,可是約會的過程你沒辦法參與都是姑娘本身安排,她可能和你看完電影以後就告訴你身份證丟了而後各回各家;因此筆者推薦使用第一種方式實例化spring容器。。。。至於緣由我已經說的夠清楚了。
到此爲止已經解釋完了問題一——AnnotatedBeanDefinitionReader這個對象實例化出來有什麼用?
那麼問題二和問題三 下篇文章討論,爲了說清楚spring當中BeanDefintion筆者已經寫了兩篇博客(根本沒有進入BeanDefintion的主題),以前說過會用三篇文章來分析,看來須要食言了,想了一下至少須要五篇才能說清楚