本文主要以spring ioc容器基本代碼骨架為切入點,理解ioc容器的基本代碼組件結構,各代碼組件細節剖析將放在后面的學習文章里。
http://www.iteye.com/topic/1113459
關于IOC容器
IoC容器:最主要是完成了完成對象的創建和依賴的管理注入等等。
先從我們自己設計這樣一個視角來考慮:
所謂控制反轉,就是把原先我們代碼里面需要實現的對象創建、依賴的代碼,反轉給容器來幫忙實現。那么必然的我們需要創建一個容器,同時需要一種描述來讓容器知道需要創建的對象與對象的關系。這個描述最具體表現就是我們可配置的文件。
對象和對象關系怎么表示?
可以用xml,properties文件等語義化配置文件表示。
描述對象關系的文件存放在哪里?
可能是classpath,filesystem,或者是URL網絡資源,servletContext等。
回到正題,有了配置文件,還需要對配置文件解析。
不同的配置文件對對象的描述不一樣,如標準的,自定義聲明式的,如何統一? 在內部需要有一個統一的關于對象的定義,所有外部的描述都必須轉化成統一的描述定義。
如何對不同的配置文件進行解析?需要對不同的配置文件語法,采用不同的解析器。
基于以上問題,對應過來,剛好是 spring ioc 容器抽象的的幾個主要接口:
Resource
BeanDefinition
BeanDefinitionReader
BeanFactory
ApplicationContext
以上五個都是接口,都有各式各樣的實現,正是這5個接口定義了spring ioc容器的基本代碼組件結構。而其組件各種實現的組合關系組成了一個運行時的具體容器。
各代碼組件詳解
1.Resource
是對資源的抽象,每一個接口實現類都代表了一種資源類型,如ClasspathResource、URLResource,FileSystemResource等。每一個資源類型都封裝了對某一種特定資源的訪問策略。它是spring資源訪問策略的一個基礎實現,應用在很多場景。
具體可以參考文章:
Spring 資源訪問剖析和策略模式應用
http://www.ibm.com/developerworks/cn/java/j-lo-spring-resource/index.html
2.BeanDefinition
用來抽象和描述一個具體bean對象。是描述一個bean對象的基本數據結構。
3.BeanDefinitionReader
BeanDefinitionReader將外部資源對象描述的bean定義統一轉化為統一的內部數據結構BeanDefinition。對應不同的描述需要有不同的Reader。如XmlBeanDefinitionReader用來讀取xml描述配置的bean對象。
4.BeanFactory
用來定義一個很純粹的bean容器。它是一個bean容器的必備結構。同時和外部應用環境等隔離。BeanDefinition是它的基本數據結構。它維護一個BeanDefinitions Map,并可根據BeanDefinition的描述進行bean的創建和管理。
5.ApplicationContext
從名字來看叫應用上下文,是和應用環境息息相關的。沒錯這個就是我們平時開發中經常直接使用打交道的一個類,應用上下文,或者也叫做spring容器。其實它的基本實現是會持有一個BeanFactory對象,并基于此提供一些包裝和功能擴展。為什么要這么做呢?因為BeanFactory實現了一個容器基本結構和功能,但是與外部環境隔離。那么讀取配置文件,并將配置文件解析成BeanDefinition,然后注冊到BeanFactory的這一個過程的封裝自然就需要ApplicationContext。ApplicationContext和應用環境細細相關,常見實現有ClasspathXmlApplicationContext,FileSystemXmlApplicationContext,WebApplicationContext等。Classpath、xml、FileSystem、Web等詞都代表了應用和環境相關的一些意思,從字面上不難理解各自代表的含義。
當然ApplicationContext和BeanFactory的區別遠不止于此,有:
1. 資源訪問功能:在Resource和ResourceLoader的基礎上可以靈活的訪問不同的資源。
2. 支持不同的信息源。
3. 支持應用事件:繼承了接口ApplicationEventPublisher,這樣在上下文中為bean之間提供了事件機制。
……
以上5個組件基本代表了ioc容器的一個最基本組成,而組件的組合是放在ApplicationContext的實現這一層來完成。
以ClasspathXmlApplicationContext 容器實現為例,其組合關系如下:
ClassPathXmlApplicationContext的refresh() 方法負責完成了整個容器的初始化。
為什么叫refresh?也就是說其實是刷新的意思,該IOC容器里面維護了一個單例的BeanFactory,如果bean的配置有修改,也可以直接調用refresh方法,它將銷毀之前的BeanFactory,重新創建一個BeanFactory。所以叫refresh也是能理解的。
以下是Refresh的基本步驟:
1.把配置xml文件轉換成resource。resource的轉換是先通過ResourcePatternResolver來解析可識別格式的配置文件的路徑
(如"classpath*:"等),如果沒有指定格式,默認會按照類路徑的資源來處理。
2.利用XmlBeanDefinitionReader完成對xml的解析,將xml Resource里定義的bean對象轉換成統一的BeanDefinition。
3.將BeanDefinition注冊到BeanFactory,完成對BeanFactory的初始化。BeanFactory里將會維護一個BeanDefinition的Map。
當getBean的時候就會根據調用BeanFactory,根據bean的BeanDifinition來實例化一個bean。當然根據bean的lazy-init、protetype等屬性設置不同以上過程略有差別。
refresh()代碼如下:
- public void refresh() throws BeansException, IllegalStateException {
- synchronized (this.startupShutdownMonitor) {
- // Prepare this context for refreshing.
- prepareRefresh();
- // Tell the subclass to refresh the internal bean factory.
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
- // Prepare the bean factory for use in this context.
- prepareBeanFactory(beanFactory);
- try {
- // Allows post-processing of the bean factory in context subclasses.
- postProcessBeanFactory(beanFactory);
- // Invoke factory processors registered as beans in the context.
- invokeBeanFactoryPostProcessors(beanFactory);
- // Register bean processors that intercept bean creation.
- registerBeanPostProcessors(beanFactory);
- // Initialize message source for this context.
- initMessageSource();
- // Initialize event multicaster for this context.
- initApplicationEventMulticaster();
- // Initialize other special beans in specific context subclasses.
- onRefresh();
- // Check for listener beans and register them.
- registerListeners();
- // Instantiate all remaining (non-lazy-init) singletons.
- finishBeanFactoryInitialization(beanFactory);
- // Last step: publish corresponding event.
- finishRefresh();
- }
- catch (BeansException ex) {
- // Destroy already created singletons to avoid dangling resources.
- beanFactory.destroySingletons();
- // Reset 'active' flag.
- cancelRefresh(ex);
- // Propagate exception to caller.
- throw ex;
- }
- }
- }
以上的obtainFreshBeanFactory是很關鍵的一個方法,里面會調用loadBeanDefinition方法,如下:
- protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
- // Create a new XmlBeanDefinitionReader for the given BeanFactory.
- XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
- // Configure the bean definition reader with this context's
- // resource loading environment.
- beanDefinitionReader.setResourceLoader(this);
- beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
- // Allow a subclass to provide custom initialization of the reader,
- // then proceed with actually loading the bean definitions.
- initBeanDefinitionReader(beanDefinitionReader);
- loadBeanDefinitions(beanDefinitionReader);
- }
LoadBeanDifinition方法很關鍵,這里特定于整個IOC容器,實例化了一個XmlBeanDefinitionReader來解析Resource文件。關于Resource文件如何初始化和xml文件如何解析都在
- loadBeanDefinitions(beanDefinitionReader);
里面的層層調用完成,這里不在累述。
小結
Spring的擴展性是毋庸置疑的,學習spring的設計是一個很好的實踐理論結合。主要個人覺得有幾點:
1. 框架頂層的設計有著很好的抽象,遵循面向接口編程的規范。Resource、BeanFactory、ApplicationContext都是非常好的接口抽象,非常明確的定義了該組件的一些功能。
2. 利用組合模式。
3. 個組件的實現里大量使用了模板方法模式,提升了同一組件代碼的復用性。
4. 各種設計保留了擴展的接口,很多基于spring的框架都可以很容易的介入實現了自己的一些擴展。
5. 框架里采用里很多經典的設計模式,如代理、裝飾、策略等等。