1. 程式人生 > >BeanDefinition的定位,載入和註冊

BeanDefinition的定位,載入和註冊

1. 總覽

   我們知道,IOC容器的工廠化管理實現了控制反轉,將Bean的依賴的關係從物件本身解耦,將控制權交由外部工廠進行處理。從而使程式碼更加靈活,所以IOC容器的實現至關重要。IOC容器實際上是將BeanDefinition處理封裝成Spring內部的Bean規則的資料結構。其過程可以分成資源的定位解析註冊refresh()是Spring中比較核心的方法,Spring所有的初始化都在這個方法中完成。

1. public void refresh() throws BeansException, IllegalStateException {  

2.         synchronized

 (this.startupShutdownMonitor) {  

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容器中設定好,最後是啟動讀取器來完成BeanDefinitionIOC容器中的載入。

接下來就是loadBeanDefinitions(XmlBeanDefinitionReader reader)的實現方法。

在這個方法呼叫了reader.loadBeanDefinitions(Resource[] resources)方法

之前我們說過,Resource物件封裝了對XML檔案的I/O操作,所以讀取器可以在開啟I/O流後得到XML的檔案物件,之後就可以按照SpringBean定義規則對這個XML文件樹進行解析了。這個解析是交給BeanDefinitionParserDelegate來完成。

SpringBeanDefinition是怎樣按照SpringBean語意要求進行解析並轉化為容器的內部資料結構的,這個過程是在registerBeanDefinition(doc,resource)中完成的。

Bean的載入分成兩個部分,首先呼叫了XML的解析器得到document物件。之後通過documentReader這個方法將其按照SpringBean規則進行解析。對BeanDefinition的處理結果由BeanDefinitionHolder物件進行持有,這個物件除了持有BeanDefinition物件之外,還持有其他與BeanDefinition的使用相關的資訊。比如Bean的名字,別名集合等。

Bean元素進行解析的過程,也就是BeanDefinition依據XML<bean>定義被建立的過程。這個Bean可以看成是對<bean>定義的抽象。

4. BeanDefinitionIOC容器中的註冊。

我們知道,經過載入和解析之後,BeanDefinition已經在IOC容器中建立起了自己的資料結構以及相關的資料表示。但這些資料還不能供IOC容器使用,還需要進行註冊。在IOC容器中,是通過一個HashMap來持有所有的載入的BeanDifinition的。

註冊的過程並不複雜,就是把解析得到的BeanDefinition設定到hashMap中。