ico源碼分析:
-
IOC初始化裆蒸,創(chuàng)建 Bean 容器
1:Resource定位:ClassPathReource resource = new ClassPathReource("bean.xml");
2:BeanDefinition的載入和解析: DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); , XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
3:BeanDefinition注冊: reader.loadBeanDefinition(resource);
-
圖解:
-
Reource定位
- 主要是兩個接口:ResourceSpring中的統(tǒng)一資源抽象接口;ResourceLoader是統(tǒng)一的資源加載抽象接口
- 具體的方法實現(xiàn)我不會說,只需要知道接口的具體實現(xiàn)類會將外部的資源加載到Spring中,當(dāng)然是以被封裝的形式存在.(resource->EncodedResource).最后會以EncodedResource對象的形式用于bean的后續(xù)初始化.
-
BeanDefinition的載入和解析
- EncodedResource并不是直接就轉(zhuǎn)換成BeanDefinition對象,而是要先轉(zhuǎn)換成Document對象,然后再轉(zhuǎn)換為BeanDefinition對象.這樣做的原因也很簡單,就是Document更方便.
- 從EncodedResource到Document的轉(zhuǎn)換過程有很多細(xì)節(jié)(比如文件解析器,錯誤處理,命名空間的問題),這里暫時將這個過程看做黑盒.
- Document -> BeanDefinition的過程其實主要是和對Document的操作有關(guān),也就是對xml標(biāo)簽的處理,有默認(rèn)標(biāo)簽和自定義標(biāo)簽,不詳細(xì)說.
-
注冊BeanDefinition
- 其實就是一些Map,BeanDefinition作為value,key是beanName的map有存儲BeanDefinition的Map贿条,所謂注冊,就是添加進(jìn)入這個Map的過程,這里的key是beanName亭病。
初始化的過程就是這樣,用一張圖結(jié)束吧:
-
Bean實例化
- 實例化時機(jī)分為很多種,一是使用BeanFactory和ApplicationContext的區(qū)別,二是Bean單例和多例的區(qū)別,三是單例情況下設(shè)置延遲加載的區(qū)別;如果是非延遲加載,那么會在容器啟動時實例化bean.如果是延遲加載,那么Bean的加載就是第一次顯示或隱式的調(diào)用getBean方法(該方法調(diào)用doGetBean方法),所以從這個方法開始:
- 1.轉(zhuǎn)換beanName,因為getBean傳入的name不一定是beanName,可能傳入的是aliasName或FactoryBean
- 2.從緩存中獲取bean,這里需要根據(jù)bean的作用域分一下情況:
- 單例:spring的IOC只會保存一個對象實例,所有該對象的引用都共享這個實例,Spring容器只會創(chuàng)建唯一一個實例,保存在緩存中,并對該bean的后續(xù)請求和引用都會返回到該緩存的對象實例中。
- 多例:每次對bean的請求都會返回一個新的實例
- 其他:
- request : 每次http請求都會有一個bean實例
- session: 一個session中,一個bean定義對應(yīng)一個bean實例
- 所以,從緩存中獲取的bean一定是單例的,這里的緩存也是一個Map,名為singletonObjects,同層次還有兩個Map,一個是earlySingletonObjects,另一個是singletonFactories.分別保存早期單例對象和bean工廠.之所以使用三個緩存,是為了解決bean之間的循環(huán)依賴.
- 3.緩存中沒有找到的話,會在父容器的緩存中尋找吸重。
- 4.如果沒有父容器或者父容器中也沒有就會創(chuàng)建bean實例對象.這里的方法是createBean蚕涤,如果createBean維持的一個緩存中沒有對應(yīng)bean的beanWrapper,這里會先創(chuàng)建BeanWrapper,這是一個對要實例化Bean的包裝.創(chuàng)建BeanWrapper的方法是createBeanInstrance,同時,這個方法也是非單例bean的創(chuàng)建實例的方法.使用的就是初始化時創(chuàng)建的BeanDefinition,大致的步驟就是確定構(gòu)造方法,然后實例化.實現(xiàn)起來有很多細(xì)節(jié),比如如果有動態(tài)代理,就需要使用cglib來實例化,否則使用反射實例化淹父≡朐#回到createBean方法,得到BeanWrapper對象后,就要進(jìn)行一系列的處理,主要是屬性注入和循環(huán)依賴的處理.然后初始化Bean蹲盘。初始化Bean主要是為了完成用戶設(shè)定,例如激活A(yù)ware方法,應(yīng)用后置處理器方法,激活自定義的方法.
- 5.得到了bean實例,但是并沒有結(jié)束,無論是從單例緩存中獲取的或者createBean中創(chuàng)建的bean,都只是bean實例,還需要從bean實例中獲取對象.這里很難理解,bean實例難道不是對象嗎,我看的博客中都是這樣說的,但是從我的理解來看,這里的bean實例其實是FactoryBean實例,不知道FactoryBean的可以了解一下.總之,最后需要從FactoryBean中拿到對象,其實就是調(diào)用了getObject方法,不過框架嘛,你懂得,總是需要處理很多情況,所以嵌套的比較深,可以從getObjectForBeanInstance和getObjectFromFactoryBean兩個方法入手自己看一下。
-
推薦閱讀: