BeanDefinition的定位,載入和註冊
1. 總覽
我們知道,IOC容器的工廠化管理實現了控制反轉,將Bean的依賴的關係從物件本身解耦,將控制權交由外部工廠進行處理。從而使程式碼更加靈活,所以IOC容器的實現至關重要。IOC容器實際上是將BeanDefinition處理封裝成Spring內部的Bean規則的資料結構。其過程可以分成資源的定位,解析,註冊。refresh()是Spring中比較核心的方法,Spring所有的初始化都在這個方法中完成。
1. public void refresh() throws BeansException, IllegalStateException {
2. synchronized
3. //準備啟動spring容器,設定容器的啟動日期和活動標誌
4. prepareRefresh();
5.
6. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//獲得容器ApplicationContext的子類BeanFactory。步驟如下:如果已經有了BeanFactory就銷燬它裡面的單例Bean並關閉這個BeanFactory。2.建立一個新的BeanFactory。3.對這個BeanFactory進行定製(customize),如allowBeanDefinitionOverriding,allowCircularReferences。4.裝載BeanDefinitions(讀取配置檔案,將xml轉換成對應得BeanDefinition)。5.檢查是否同時啟動了兩個BeanFactory。
7.
8. // 通過XmlBeanDefinitionReader載入bean定義.
9. prepareBeanFactory(beanFactory);
10.
11. try {
12. // Allows post-processing of the bean factory in context subclasses.
13. postProcessBeanFactory(beanFactory);
14.
15. // Invoke factory processors registered as beans in the context.
16. invokeBeanFactoryPostProcessors(beanFactory);
17.
18. // 註冊 bean processors
19. registerBeanPostProcessors(beanFactory);
20.
21. // Initialize message source for this context.
22. initMessageSource();
23.
24. // Initialize event multicaster for this context.
25. initApplicationEventMulticaster();
26.
27. // Initialize other special beans in specific context subclasses.
28. onRefresh();
29.
30. // Check for listener beans and register them.
31. registerListeners();
32.
33. // Instantiate all remaining (non-lazy-init) singletons.
34. finishBeanFactoryInitialization(beanFactory);
35.
36. // Last step: publish corresponding event.
37. finishRefresh();
38. }
39.
40. catch (BeansException ex) {
41. // Destroy already created singletons to avoid dangling resources.
42. destroyBeans();
43.
44. // Reset 'active' flag.
45. cancelRefresh(ex);
46.
47. // Propagate exception to caller.
48. throw ex;
49. }
50. }
51. }
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
此方法比較關鍵,程式碼也比較多
Java程式碼
1. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
2. refreshBeanFactory();
3. ConfigurableListableBeanFactory beanFactory = getBeanFactory();
4. if (logger.isDebugEnabled()) {
5. logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
6. }
7. return beanFactory;
8. }
refreshBeanFactory();
Java程式碼
1. @Override
2. protected final void refreshBeanFactory() throws BeansException {
3. if (hasBeanFactory()) {
4. destroyBeans();
5. closeBeanFactory();
6. }
7. try {
8. DefaultListableBeanFactory beanFactory = createBeanFactory();
9. beanFactory.setSerializationId(getId());
10. customizeBeanFactory(beanFactory);
11. loadBeanDefinitions(beanFactory);
12. synchronized (this.beanFactoryMonitor) {
13. this.beanFactory = beanFactory;
14. }
15. }
16. catch (IOException ex) {
17. throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
18. }
19. }
loadBeanDefinitions(beanFactory);
Java程式碼
1. @Override
2. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
3. // Create a new XmlBeanDefinitionReader for the given BeanFactory.
4. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
5.
6. // Configure the bean definition reader with this context's
7. // resource loading environment.
8. beanDefinitionReader.setEnvironment(this.getEnvironment());
9. beanDefinitionReader.setResourceLoader(this);
10. beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
11.
12. // Allow a subclass to provide custom initialization of the reader,
13. // then proceed with actually loading the bean definitions.
14. initBeanDefinitionReader(beanDefinitionReader);
15. loadBeanDefinitions(beanDefinitionReader);
16. }
loadBeanDefinitions(beanDefinitionReader);裝載xml檔案到XmlBeanDefinitionReader的過程
Java程式碼
1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
2. Resource[] configResources = getConfigResources();
3. if (configResources != null) {
4. reader.loadBeanDefinitions(configResources);
5. }
6. String[] configLocations = getConfigLocations();
7. if (configLocations != null) {
8. reader.loadBeanDefinitions(configLocations);
9. }
10. }
IOC的初始化過程可以分為定位,解析和註冊三個方面。下面我將解釋這三個方面:
(1)定位:Resource介面對各種形式的BeanDefinition的使用提供了統一介面。BeanDefinition是對Bean定義資訊的抽象。(對此處的抽象進行解釋:計算機的資料是對現實資料的抽象,將現實事物所具有的屬性和行為進行描述,將其描述為計算機可以識別的編碼資訊。而BeanDefinition也是對Bean的抽象,它將Bean的各個資訊抽象成一個數據型別,當然BeanDefintion定義的方式有很多,Spring中主要是以XML的形式進行定義BeanDefinition。這個BeanDefinition對這個Bean的一些屬性進行了解析,例如其className等等)。
(2)載入:將Bean表示成IOC容器內部的資料結構。BeanDefinition是POJO物件在IOC容器中的抽象。通過BeanDefinition定義的資料結構,使IOC容器能夠方便地對POJO物件也就是Bean進行管理。
(3)
註冊:把解析得到的BeanDefinition向IOC容器中進行註冊,將BeanDefintion注入到一個HashMap中去。
2.BeanDefinition的定位:
①首先定義一個Resource來定義容器使用的BeanDefinition,BeanDefinition的獲取是從傳入的xml檔案中獲取的。
②Resource資源不能直接交給BeanFactory使用,需要BeanDefinition的loadBeanDefinitions()方法進行處理。
③ApplicationContext是相對BeanFactory的高階IOC容器。它將BeanFactory對Resource資源的定位,載入,註冊進行了封裝,但是降低了IOC容器的靈活性。
關於Resource的定位,方式有很多。下面我們以FileSystemXmlApplicationContext進行舉例。它具備了ResourceLoader讀入Resource定義的BeanDefinition的能力。之前我們說過,整個IOC容器的實現都是呼叫了refresh()方法。其中refreshBeanFactory()方法被呼叫,在這個方法中,通過createBeanFactory()方法構建了一個IOC容器供ApplicationContext使用,這個容器就是DefaultListableBeanFactory,同時,它啟動了loadBeanDefinitions來載入BeanDefinition。.關於具體的程式碼實現可以參考書33頁的程式碼清單2-5,以上就是BeanDefinition的定位過程,我們在獲取了Resource物件之後,就可以對BeanDefinition的載入創造IO操作的準備了
3.BeanDefinition的載入和解析
載入:整個載入過程,相當於把定義的BeanDefinition在IOC容器中轉化成一個Spring內部表示的資料結構的過程。載入IOC容器對Bean的管理和依賴注入功能的實現,是通過對其持有的BeanDefinition進行各種相關操作來完成的。
在建立IOC容器之前,如果之前已經有容器存在,那麼需要把已經存在的容器進行銷燬和關閉,保證refresh之後建立起來的都是新建立的IOC容器。下圖為BeanDefinition的載入互動過程所呼叫的方法。
在loadBeanDefinitions中,初始化了讀取器XmlBeanDefinitionReader,然後把這個讀取器在IOC容器中設定好,最後是啟動讀取器來完成BeanDefinition在IOC容器中的載入。
接下來就是loadBeanDefinitions(XmlBeanDefinitionReader reader)的實現方法。
在這個方法呼叫了reader.loadBeanDefinitions(Resource[] resources)方法
之前我們說過,Resource物件封裝了對XML檔案的I/O操作,所以讀取器可以在開啟I/O流後得到XML的檔案物件,之後就可以按照Spring的Bean定義規則對這個XML文件樹進行解析了。這個解析是交給BeanDefinitionParserDelegate來完成。
Spring的BeanDefinition是怎樣按照Spring的Bean語意要求進行解析並轉化為容器的內部資料結構的,這個過程是在registerBeanDefinition(doc,resource)中完成的。
Bean的載入分成兩個部分,首先呼叫了XML的解析器得到document物件。之後通過documentReader這個方法將其按照Spring的Bean規則進行解析。對BeanDefinition的處理結果由BeanDefinitionHolder物件進行持有,這個物件除了持有BeanDefinition物件之外,還持有其他與BeanDefinition的使用相關的資訊。比如Bean的名字,別名集合等。
對Bean元素進行解析的過程,也就是BeanDefinition依據XML的<bean>定義被建立的過程。這個Bean可以看成是對<bean>定義的抽象。
4. BeanDefinition在IOC容器中的註冊。
我們知道,經過載入和解析之後,BeanDefinition已經在IOC容器中建立起了自己的資料結構以及相關的資料表示。但這些資料還不能供IOC容器使用,還需要進行註冊。在IOC容器中,是通過一個HashMap來持有所有的載入的BeanDifinition的。
註冊的過程並不複雜,就是把解析得到的BeanDefinition設定到hashMap中。