spring步步前行(IOC)-Spring beanFactory詳解(一)
beanFactory組成
在接觸spring的ioc的時候 factory與applicationContext它們倆真真攪得糊里糊塗,下定決心重新認識下它們倆
我們以DefaultListableBeanFactory爲例:
其成員變量beanDefinitionMap從後面的分析有介紹,這就是存放我們xml定義的bean信息
其繼承了beanFactory的同時還實現一些接口
ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable
我們關注BeanDefinitionRegistry接口,字面意思是bean定義註冊器
從其方法上我們可以看出其具備register/remove等beanDefinition操作
這就是一個BeanFactory的大概功能
由於內容繁多,我們從兩個問題出發,去了解beanfactory
- beanFactory是怎麼初始化
- beanFactory如何去加載xml中的bean
準備工作
-
創建一個bean
-
public class TargetBean { public void hellobean() { System.out.println("hello bean..."); } }
-
聲明bean.xml 並藉助ClassPathXmlApplicationContext我們get一下我們的目標tartbean
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <bean id="targethello" class="org.tutor.spring.ioc.TargetBean"></bean> </beans>
-
ApplicationContext context = new ClassPathXmlApplicationContext('bean.xml'); TargetBean bean = context.getBean("targethello"); bean.hellobean();
-
運行結果很明顯我們看到了控制檯打印的“hello bean…"的結果,這時我們暫時將bean.xml拿掉我們看看它的報錯信息,如下:
這裏的打印向我們透露的幾個關鍵類
AbstractApplicationContext
AbstractXmlApplicationContext
AbstractBeanDefinitionReader
等
beanFactory是初始化
我們debug調試運行看看beanFactory如何初始化的:
-
首先ClassPathXmlApplicationContext內將我們傳入的configLocation(即bean.xml)用String[]包裝了一下
-
接着在構造內進行setConfigLocations操作
該方法在AbstractRefreshableConfigApplicationContext中實現,對該類中的configLocations進行賦值,如下
可能有人對resolvePath想深入瞭解,其實該操作就是對我們傳入的location中的佔位符進行處理,有興趣的童鞋,可以自行深入瞭解一下
-
setConfigLocations之後就是ClassPathXmlApplicationContext的refresh操作,該refresh方法實現在AbstractApplicationContext中,如下:
這裏也就是初始化SpringBeanFactory的核心部分了
-
我們先看obtainFreshBeanFactory()如何獲取一個BeanFactory
首先refreshBeanFactory中,若存在Factory即容器,先銷燬內裏的bean,spring保證只有一個容器,然後在線程安全下,爲beanFactory賦值
-
createBeanFactory()內部爲我們new了一個DefaultListableBeanFactory,然後爲這個DefaultListableBeanFactory設置序列化id,最後定製化beanFactory
對於定製化而言,其實就是爲一些解析器Resolver設置BeanFactory,以及對beanFactory的一些功能進行設置,好比是否可以循環引用,允許bean定義覆蓋等
-
最後也就到了最關鍵的地方loadBeanDefinitions(beanFactory),我們先看看loadBeanDefinitions,如下:
-
加載bean
-
loadBeanDefinitions
從上面loadBeanDefinitions方法中,我們可以看到其創建了一個XmlBeanDefinitionReader,並將之前準備好的beanFactory傳入,我們看看XmlBeanDefinitionReader構造:
這裏的Registry就是我們的beanFactory了,我們在篇幅最初說的beanFactory實現了BeanDefinitionRegistry的接口,也就具備了註冊beanDefinition等功能。
看其構造我們發現其將registry傳入父類處理了,我們在看看其將registry如何處理
由於我們傳入的是beanFactory所以這裏都是默認配置,即PathMatchingResourcePatternResolver和StandardEnvironment
接着外部重新setEnvironment和setResourceLoader以及setEntityResolver,即StandardEnvironment和ClassPathXmlApplicationContext以及ResourceEntityResolver解析器
最後進行初始化BeanDefinitionReader
-
加載
有了之前創建的xmlBeanDefinition,緊接着我們通過AbstractXmlApplicationContext內部實現的loadBeanDefinitions開始加載bean
getConfigLocations就是我們最開始ClassPathXmlApplicationContext幫我們包裝的那個bean.xml
-
xmlBeanDefinitionReader.loadBeanDEfinitions根據我們的bean.xml去加載我們的bean,經過一系列加載,最終進入XmlBeanDefinitionReader的doLoadBeanDefinitions進行registerBeanDefinitions操作
-
registerBeanDefinitions想容器中註冊我們的bean
這時DefaultBeanDefinitionDocumentReader的regitsterBeandefinition進行xml解析進行bean處理
循環每一個元素,分別解析
經過xml解析
-
藉助BeanDefinitionReaderUtils.registerBeanDefinition 完整bean的註冊
通過我們beanfactory進行registerBeanDefinition,由於beanfactory內的registerbeanDefinition方法較長,在這僅羅列主要部分
由此我們可以看出beanFactory維護的beanDefinitionMap存放了我們加載的bean信息
即key爲我們bean.xml中爲bean設置的id,value是bean信息
- 注意BeanDefinitionReaderUtils.registerBeanDefinition中,如果有別名也會進行別名註冊,此處不再進行詳細分析,有興趣的同學,可自行深入瞭解
-
由此我們以上初始化及加載兩個問題,解析至此
這裏只講述了refresh中的obtainBeanFactory以前的部分,refresh這塊我會再單獨寫一篇詳細關於這個refresh核心方法的分析