IoC容器主要用於管理Bean的生命週期和對象間的關係,經過依賴注入(DI)對容器中的Bean所須要依賴的其餘對象進行注入。而這一切都是在Ioc容器裏邊進行的,假設A對象依賴B對象,若是IoC容器裏只有A沒有B,那麼將會拋出bean找不到的異常;或者說A對象不在IoC容器,而B對象在IoC容器,那麼將達不到自動注入的效果。java
web
這裏是父級抽象類裏的方法,方法實現自更上一級的ConfigurableApplicationContext接口。spring
編程
而後會有一個刷新前的準備操做,好比記錄刷新開始時間啦,一些狀態變量啦,打印日誌啦。緩存
session
OK,能夠看到它屬性解析器裏有el表達式的 ${ } 和 : 符號,並且屬性源列表裏有兩個類型的屬性源,打開其中一個,有不少K/V鍵值對,結合各類類、屬性的各類語義,看來我以前的猜測無誤,確實是用於把變量解析出值的。多線程
行,下一個。app
1 // Tell the subclass to refresh the internal bean factory. 2 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
跟蹤一下obtainFreshBeanFactory();jvm
函數式編程
兜兜轉轉,終於來到了解析xml定義的bean的代碼位置。
OK,解讀這一段:
首先,獲取<bean>
接着,獲取<bean>節點上的name屬性;那麼,name屬性呢,會被做爲別名,多個別名能夠容許用英文半角符號下的逗號和分號進行分割(, ;)。
接下來,就是以(,;)分割name屬性上的值,將每一個別名都分割出來,雖然它在內部對分割出來的每一個別名作了trim操做,但我仍是建議不要留有空格。
再接着,肯定beanName,若是id屬性有的話,就用id屬性,沒有的話,就會從別名中抽出第一個別名做爲beanName,我說的抽出,就是它裏邊的aliases.remove(0)。
而後呢,會進行一個對beanName的查重操做,這裏邊能夠看出,beanName和aliase是存放在同一個命名空間的(Set<String>集合),所以,在上一步aliases須要用remove(),不然本身就會拋名稱重複異常。
接下來都差很少,就是一些屬性的解析,子節點的解析。而後都封裝到org.springframework.beans.factory.support.DefaultListableBeanFactory的beanDefinitionMap屬性裏,固然還有不少其它的細節我沒有去深究,水平有限,太細了腦殼會爆的。
進入方法後也能看到,Spring的源碼裏有用到Lambda表達式,以前有遇到過Java8以前版本Lambda報錯,因而這裏我嘗試了把IDEA的Language level改成了7,發現也能正常運行,仔細回想,以前碰到的Lambda報錯是由於IDEA容許寫Lambda,可是因爲選擇的java編譯器是Java8版本前的版本,因而編譯不經過。那麼這麼一想,我現在加入的依賴其實是Spring已經編譯好的jar包,也就是說Lambda表達式已經被編譯成了字節碼文件,即便把運行環境切換回Java8以前的版本,也是能夠運行的。那麼我大膽的猜想,Java8和以後推出的一些語法糖在編譯後,一樣能在低版本的jvm上運行(這個離題了,以後再試,到時候順便去看看官方文檔)。
我們繼續分析上面的代碼,最後一行就是初始化單例的操做了,那麼在它的上一行,還有一個凍結配置的操做,代碼粘貼出來,都能看懂,凍結後具體有什麼用,咱先不看。
循環迭代全部定義的Bean,獲取到Bean在Root容器中的定義 (這塊的合併定義有點東西,有空再看仔細),接着就是判斷Bean的類型是否爲org.springframework.beans.factory.FactoryBean接口的派生類,若是是,還會通過一些系統權限特殊處理。固然,最後都會到達getBean(beanName)。
簡單理解下:
1.嘗試獲取單例,若是單例已經存在了,那麼作一些驗證後沒有問題直接返回這個單例,裏邊的邏輯還涉及一些工廠Bean和普通Bean的選擇問題,也挺複雜的,就不細說。這其實已經能夠證實了我上面的猜想了。
2.1若是示例並不存在,那麼首先檢查它是否是在構建中,若是已經在構建中了這裏還來構建, 那可能已經進入了一個循環構建的狀態了,這時候就會直接拋出【BeanCurrentlyInCreationException】,後邊我會針對這個異常進行測試。
2.2先獲取父級BeanFactory,若是存在父級的BeanFactory而且自身有沒有該beanName對應的BeanDefinition的話,那用父級的BeanFactory來提供這個Bean,若是一直到Root級的容器也提供不了這個Bean,那就是【NoSuchBeanDefinitionException】了。那麼這裏的話能夠參考上面的第7大點,建立BeanFactory的時候,會將父級容器裏的BeanFactory做爲本身的patentBeanFactory,那咱們這裏的是Root級別的容器,BeanFactory也就是Root級別的,因此只能本身構建了。
2.3判斷下是否要類型檢查,通常都是false,我看來一下,只有一個地方用了true,位置在方法org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean。 咱不深究,就以如今是false的狀態繼續吧,會執行markBeanAsCreated(beanName),這裏在真正構造Bean以前,先記錄一下Bean已經構建了,而後還把mergedBeanDefinitions集合裏的Bean的定義給remove掉了。
2.4分析Bean定義開始,調用的getMergedLocalBeanDefinition(beanName),哇塞,上一步剛吧BeanDefinition從mergedBeanDefinitions集合中remove掉,這一步又給加回去了,感受刪掉那一步有些畫蛇添足,就像是個BUG,不過倒不會引發什麼異常。
接下來注意了注意了,這裏要開始構建了,若是忽略掉多級容器,多線程什麼什麼的,我以爲這個位置算是最核心部分了,在我看來接下來這塊就是IoC和DI的具體實現。
2.5從RootBeanDefinition定義中獲取到構建依賴(dependsOn),這個dependsOn,其實就是在配置<bean>的時候能夠配置一個"depend-on"這麼一個屬性,裏邊只能配置其餘的beanName,若是這個屬性有值,就會先等待依賴的beanName先構建好,再繼續構建自身,若是配置的是本身,或者是依賴自身的其餘類,那麼就會陷入死循環,拋出異常好比:【BeanCreationException】。
2.6若是存在依賴,那麼註冊依賴關係,這依賴關係是雙向維護的,若是A依賴B,那麼A所需依賴裏有B,B的被他依賴裏有A。
2.7而後先把所需依賴給初始化了,也就是A依賴B,那麼先把B給初始化了,這裏就算是爲DI作前置準備了。
2.8接着,就判斷Bean的scope,若是是singleton,getSingleton
若是是prototype,那就從新構建一個Bean的實例。
這裏咱先關注singleton,能夠看到getSingleton()穿了兩個參數,一個是beanName,一個是Lambda表達式,嗯,就是構建了一個匿名內部類的實例做爲參數。嗯,函數式編程槓槓的。
2.8.1這裏一樣是,若是單例已經存在了,就直接返回,若是沒有存在,就調用專屬的ObjectFactory#getObject構建一個實例再返回。
再跟蹤doCreateBean,
在一連串繁瑣的處理後,終於仍是來到了BeanUtils……
先給構造器設置爲可訪問的,還檢查一下是否爲Kotlin的類,是就用Kotlin的方式構造實例,不是就用調用原生的newInstance。唉,再深刻就是反射的源代碼了,前段時間剛看到一篇文章說反射的對象調用超過必定次數後會被生成class字節碼加載到jvm裏,今天看到了那段代碼,可是讓腦子緩緩吧,改天再研究反射的實現。
最後,對象構建完成後,還有屬性的注入。
其中populateBean則是對屬性進行賦值的,一直找到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValues
會看到遍歷屬性賦值這一段:
若是有關聯(即ref),則會在valueResolver.resolveValueIfNecessary(pv, originalValue);內進行檢查。
再跟蹤到resolveReference裏:
能夠看到它會在這裏getBean,若是ref的bean已存在,天然會直接獲得,若是沒有存在,則會先初始化。嗯,而後上面的初始化操做又來一遍。
備註:
用於類型轉換的類型修改器默認只有overriddenDefaultEditors,共12個,均爲與IO相關的好比URL、InputSteam。
基本類型的修改器在首次查到沒有時,纔會建立,共47個,好比int、long、String,當前Spring版本爲5.1.7.RELEASE。
① scope="singleton"時,Spring能處理好這個關係,成功容許。
解析成BeanDefinition的時候是不拋異常的,只有在運行到構建dependOn時,纔會拋出異常【BeanCreationException】,不管scope是prototype仍是singleton。
一樣,無論scope是prototype仍是singleton,都會拋出異常【BeanCurrentlyInCreationException】。
雙方是 都是scope="prototype"時,拋出異常【BeanCurrentlyInCreationException】。只要有一方是 scope="singleton",則可正常運行。
那Role必須是單例的狀況下,才能正常運行了。
從容器結構來看,理論上IoC容器能夠達到無限嵌套,在子容器維護着父級容器的關係,父子容器各自定義的Bean的單例都會緩存在各自的BeanFactory的singletonObjects裏,當子容器在singletonObjects中找不到Bean時,會往父容器裏找,或者說子容器中能夠定義新的Bean屏蔽掉父級的Bean,使得切換不一樣的Bean實現能夠更加靈活。可是呢,父容器由於沒有維護與子容器的關係,所以父容器裏是沒法經過getBean獲取到子容器的Bean的。
Bean的定義上,會將Bean的構建條件都解析封裝到BeanDefinition中,纔開始初始化單例。定義時,要規避死循環通常的依賴,也就是在實例化Bean前,避免依賴關係又回當前Bean。
② 容器初始化大體流程
水平有限,若是有哪裏寫的不對的,歡迎指出,我會及時改正,避免誤導你們。