深刻理解Spring IOC(七) 、 總結,含常見面試題

總結

咱們在以前的文章中,對spring加載bean的流程作了詳細的講解,咱們知道要將xml中的各個節點解析成真正的bean要通過下面的主要過程:java

一、將xml中的信息解析成BeanDefinition,這其中,XmlBeanDefinitionReader專門負責去讀取xml,而且將xml中的信息解析成BeanDefinition放到DefaultListableBeanFactory中。git

二、BeanFactoryPostProcessor去根據本身的須要修改BeanFactory中已經加載好的BeanDefinitiongithub

三、拿到全部的beanName,一個一個的去對單例調用getBean方法去初始化,這個過程分爲了如下步驟:面試

  • (1)將這個beanName對應的BeanDefinition轉化成RootBeanDefinition,這也是出於初始化的須要,RootBeanDefinition纔是標準的初始化流程須要的BeanDefinition,裏面包含了不少解析過程的緩存,以及其餘重要的屬性,這步主要是DefaultListableBeanFactory的preInstantiateSingletons中作的。
  • (2)接下來是要看是經過工廠方法仍是構造方法去建立這個對象,若是有工廠方法,則去解析須要的工廠方法和其參數,若是沒有工廠方法,則須要使用構造方法去建立對象,這時候就須要判斷究竟使用哪一個構造方法,判斷流程爲先使用AutowiredAnnotationBeanPostProcessor中的determineCandidateConstructors方法來肯定候選的構造方法,這裏肯定的原則主要是根據註解來肯定,若是你有多個@Autowired標註的構造方法,那麼只能有一個的required爲true,determineCandidateConstructors方法可能會返回多個方法,這時候須要根據參數匹配的原則來匹配最合適的構造方法並解析其參數,這時候可能會觸發其餘bean的初始化(createArgumentArray方法),最合適的構造方法和參數都拿到後,則去調用它建立對象。這一步主要是在AbstractAutowireCapableBeanFactory中的createBeanInstance中完成。
  • (3)獲取到上一步建立的對象以後,則要進行屬性填充的工做,這時候主要處理的是@AutoWired綁定的屬性和方法,@Value綁定的屬性,@Inject綁定的屬性。這個工做主要是在AbstractAutowireCapableBeanFactory中的populateBean方法中完成。
  • (4)屬性填充完畢以後,則去執行各類初始化的方法,其中有InitializingBean,以及@PostConstruct和在xml中定義的init-method都會在這裏執行,這個工做是在AbstractAutowireCapableBeanFactory中的initializeBean中完成。
  • (5)判斷是否是須要提早曝光bean。

相關面試題

話說其實我是不怎麼願意講面試題的,由於我認爲你把源碼看懂以後根本就不須要看這樣的東西,可是大多數讀者貌似都有這樣的需求,因此也就沒辦法,說說最多見的一些面試題spring

順便求一波贊,祝點讚的人面試一飛沖天😄~ 編程

    1. IOC的好處:這個在個人這篇文章(深刻理解Spring IOC(一) 、統一資源的加載 )的開篇說的很清楚了,好處是解耦,具體的你能夠回過頭再去看看。
    1. Spring怎麼解決循環依賴: 循環依賴主要是經過三層緩存來解決的,三層緩存並非官方概念,固然我也不知道是誰提出來的這個概念,就姑且這麼叫吧。這三層緩存在源碼中分別是singletonObjects,earlySingletonObjects,以及singletonFactories這三個map。其中singletonObjects是用來保存建立好的成品對象,earlySingletonObjects是用來保存提早曝光出來的對象,singletonFactories是用來保存獲取半成品對象的ObjectFactory的。它們是怎麼配合起來解決循環依賴的呢?咱們來看個最簡單的例子:緩存

      有兩個類A和B,A中有類型爲B的成員屬性,B中又有類型爲A的成員屬性,注意不能是prototype,也不能是構造器依賴。當建立了一個不完整的A對象時候(其實也就是隻調用了A的構造方法而沒有進行屬性填充),這時候會把這個「不完整」的A對象包裝成ObjectFactory後放到singletonFactories中,再去作屬性填充的動做,而後就在屬性填充的時候發現了須要初始化B,就跑去初始化B去了,而後也是先建立了一個不完整的B而且包裝成ObjectFactory放到了singletonFactories後去填充屬性,這時候發現須要一個A,而後又去初始化A,可是此時singletonFactories裏面的話已經有個不完整的A的ObjectFactory了,這時候就能夠經過這個singletonFactories來獲取到半成品的A的ObjectFactory,再經過A的ObjectFactory獲取到半成品A,注意這裏還會把這個半成品A的ObjectFactory從singletonFactories移除,而後還會把半成品A放到earlySingletonObjects中去,拿到半成品A後去填充B,B建立完了以後再用建立完的B填充A,這樣A和B的建立過程就完成了springboot

爲何須要把半成品A放到earlySingletonObjects中去呢?你能夠試着結合那部分的源碼想一下A依賴B,B依賴C,C依賴D,D又依賴A、B、C的這種複雜循環依賴場景你就會明白了。另外earlySingletonObjects的設計並不只僅是爲了解決循環依賴,它是還有別的做用的(代理相關的),因此不要僅僅只是背面試題。我如今再就問你一個問題,Spring是怎樣判斷你的這兩個Class是否是循環依賴的?估計不少只是背題的人就懵了,因此只背題而根本都不看相關源碼的話,面試官老是能夠花式吊打你的,不信你到我面前來試試😈 框架

    1. bean的生命週期: 所謂生命週期,主要是涵蓋着從bean的誕生一直到被銷燬,網上還有文章扯到了BeanFactoryPostProcessor上面了,可是在BeanFactoryPostProcessor執行的時候,bean還沒變成受精卵呢你生命週期個蛋蛋,看過我文章的都知道由於此時仍是BeanDefinition而已,bean都尚未初始化。對於bean的生命週期,我認爲在《spring揭祕》這本書上的這張圖能夠說是解釋的很準確了:

爲了把這個問題說的更清楚一些,我不顧讓圖變醜的巨大危險,給圖裏的步驟上面標上了表示步驟的數字🤦‍‍,微服務

咱們一步一步來看:

第1步,建立一個"不完整"的bean對象,細節在第五篇的creatBeanInstance方法中,這是建立bean必須通過的過程(除過代理);

第2步填充屬性也是必須通過的,哪怕你沒有屬性,這一步也是要走的;

第3步這個,是先檢查你的bean是否是實現了Aware接口,沒有實現Aware那就直接跳過了,若是實現了Aware,還要檢查是哪一個Aware,而後纔會給你設置某個Aware,這裏會檢查三個Aware,分別是BeanNameAware、BeanClassLoaderAware、BeanFactoryAware這三個Aware,注意確定是沒有ApplicationContextAware的,網上說有這個Aware的怕是本身都根本沒看過源碼的;

第4步這個是BeanPostProcessor的前置處理,是必須通過的;

第5步和第6步,都是先要檢查,而後知足條件才調用,沒有的話就跳過了;

第7步這個是BeanPostProcessor的後置處理,也是必須通過的;

第8步是你這個bean必須是個DisposableBean或者實現了java的AutoCloseable接口,或者你得實現DestructionAwareBeanPostProcessor這個接口,纔會走這步,這時候會把你知足條件的bean加到disposableBeans這個map中方便後續調用(和三層緩存在一個類中);

第9步對應的是咱們在業務中的使用;

第10和第11步是對應銷燬bean,這個一般是由於AbstrctApplicationContext的close方法被調用,這時候會調用以前存在disposableBeans中的全部bean的相關銷燬的方法,也是屬於不必定會走的方法。

爲何我把圖上的文字「Bean的實例化過程」用紅框圈出來了呢?由於很明顯,這個圖不只僅包含建立了,而是還有銷燬過程,所以我以爲這個圖更應該叫作bean的生命週期而不是Bean的實例化過程纔對(我的觀點)~

不止於源碼的

到這裏,整個正篇部分就完了,後邊還有一些擴展篇沒有寫完。或許你到這裏以爲spring ioc的代碼很牛,但其實真正牛的是ioc的這種思想,它真正意義上解決了面向對象的痛點。不少人都很認同spring爲java續命了十幾年,這種說法我必定程度上是認同的,如今spring系列的東西爲咱們的開發生產帶來了太多的便利,固然也淘汰了一大批人,不少人只會crud的逐漸適應不了這種變化,也有不少人抱怨變化太快,可是若是你一直也只是會用別人封裝的東西的話那其實說真的,不淘汰你淘汰誰?

話又說回來,Spring解決了面向對象編程的痛點,那又有什麼缺點呢?咱們如今作微服務的小夥伴確定都有這樣的體驗,就是一個springboot應用,明明咱們業務代碼在裏面沒有多少,可是這個應用真正跑起來都很很費內存,這個就和spring應用在運行期間有自動裝載了不少的bean有着千絲萬縷的關係,這一點其實不太符合微服務的這個味道。

至於爲何要讀源碼,我想看到我說這些內心話的這塊的人,咱們是有一個共同的目標,那就是但願本身能成爲一個比別人更好一些的工程師。我本身是的目標即是要本身作的可以比別人更好。Ioc這部分源碼看完了以後,以爲看不少框架層面報的錯都變簡單了不少,更重要的是去看好多框架和Spring整合的源碼這裏是真的變得很容易,這並非我在裝逼,而是本身真真實實的收穫和提升。

或許你雖然看到了這裏可是前面的文章卻都還看的不太明白,這其實很正常,我仍是建議你能從我第一篇給出來的github上面把個人代碼拉下來,進去對着個人註釋跟一跟,研究源碼,歷來不可能只是看看別人的文章就能明白那裏面的東西,那是必須一遍遍的看,一遍遍的debug。也有不少讀者頗有野心,老是想在面試的時候反手把面試官按在地上摩擦一下證實證實本身,可是那絕對不是說是你背多少題就能作到的,要想經得起考驗,確定是要把內功練好的。想要變得比別人強這點恆心仍是須要有的,我看源碼看的頭昏眼花,可是最後都堅持下來了。若是有什麼問題實在不明白,也能夠和我來溝通,我若是有時間會給你講,個人郵箱是554013882@qq.com,文章有什麼問題,也能夠發到這裏來。

相關文章
相關標籤/搜索