本文的目標:
從實現的角度來認識SpringIoC容器。
觀察的角度:
從外部介面,內部實現,組成部分,執行過程四個方面來認識SpringIoC容器。
本文的風格:
首先列出SpringIoC的外部介面及內部實現所涉及到的元件列表;
其次介紹元件之間的相互關係以對整個執行過程有所把握;
然後針對每一個元件的簡單介紹,包括元件的類結構圖,核心功能描述,重要介面的重要方法描述;
接下來看SpringIoC容器實現對物件導向基本設計原則的遵守;
最後是後記部分。
術語約定:
元件:本文中的元件是指在功能概念上具有相對獨立性的功能單元,物理結構上的特徵一般由一組介面、一組抽象類、一組具體實現類、異常類、工具類所組成;
這裡的元件是一種相當狹義的描述,根據上下文的不同,元件可以有不同的表現形式,如:相對於Spring框架,SpringIoC容器就是Spring框架的一個元件,
相對於系統的整體框架設計,Spring框架就是整體框架的一個元件,這裡元件與模組的概念基本等同。
介面方法:一般定義在介面中,提供給外部呼叫的方法,介面方法最重要的在於介面提供者以清晰、簡潔的定義提供了介面使用者所必需的功能特徵;
基礎方法:首先基礎方法也是一個介面方法,但和介面方法的差別在於介面方法的直接實現依賴於基礎方法(參見BeanDefintionReader介面中的方法定義);
鉤子方法:超類留給子類需要實現或重寫的方法,
預設實現鉤子方法:超類提供預設實現,子類可以選擇是否有必要重寫,
預設空實現鉤子方法:超類提供一個空實現方法,子類可以選擇是否有必要重寫,
抽象鉤子方法:超類留給子類必須實現的方法,
內部實現方法:對某一相對獨立的處理邏輯的封裝,以便增強程式碼的可讀性、可修改性、可重用性,達到程式碼的清晰性、簡潔性。
注:
本文的原始碼基於Spring2.x。Spring的原始碼也處於演變中,但對基礎程式碼的影響並不大。
正文:
Spring IoC容器的外部介面:
ApplicationContext
BeanFactory
WebApplicationContext
BeanFactory是IoC容器的核心元件,其它元件都是在為BeanFactory提供服務.
ConfigurableBeanFactory
AutowireCapableBeanFactory
ListableBeanFactory
HierarchicalBeanFactory
AbstractBeanFactory
AbstractAutowireCapableBeanFactory
DefaultListableBeanFactory
SingletonBeanRegistry介面,
BeanDefintionRegistry介面,
Resource元件,
ResourceLoader元件,
BeanDefintion元件,
BeanDefintionReader元件,
XmlBeanDefinitionParser元件,
BeanDefintionParser元件,
NamespaceHandler元件,
NamespaceHandlerResolver元件,
BeanWrapper元件,
------------------------------------------------
ApplicationContext
ConfigurableApplicationContext
AbstractApplicationContext
AbstractRefreshApplicationContext
AbstractXmlApplicationContext
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
Lifecycle介面
ApplicationEventPublisher介面
ApplicationEventMulticaster元件
MessageSource元件
MessageSourceResolvable元件
-----------------------------------------------
WebApplicationContext
ConfigurableWebApplicationContext
AbstractRefreshWebApplicationContext
XmlWebApplicationContext
ContextLoader元件
ContextLoaderListener
ContextLoaderServlet
------------------------------------------------------------------------------------------
FactoryBean
一組回撥介面,
InitializingBean
DisposableBean
BeanPostProcessor
BeanFactoryPostProcessor
BeanNameAware
BeanFactoryAware
ResourceLoaderAware
ApplicationContextPublisherAware
MessageSourceAware
ApplicationContextAware
ApplicationContextAwareProcessor
ServletContextAware
ServletConfigAware
ServletContextAwareProcessor
------------------------------------------------------------------------------------------------
對這三個介面(ApplicationContext、BeanFactory、WebApplicationContext)的討論:
ApplicationContext介面是IoC容器概念的直接對應物,包括容器自身生命週期的管理(容器的啟動,容器的初始化,容器的銷燬)
一些便利功能的提供如:資原始檔的讀取,容器級事件的釋出。
BeanFactory介面是IoC容器的核心,其它元件都為此元件提供支援,如Resource元件,ResourceLoader元件,BeanDefintionReader元件,
BeanDefintion元件,BeanWrapper元件等。BeanFactory介面相對於容器的概念太過低階,以至於直接使用需要應對較複雜的API。
WebApplicationContext介面提供IoC容器對Web環境的支援,與ServletAPI的整合工作。普通Java應用程式選擇IoC容器使用ApplicationContext,
Web環境下的IoC容器使用WebApplicationContext。
下面來關注這兩行程式碼的執行都發生了那些事情,以瞭解容器的整個執行過程。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Foo foo = (Foo)applicationContext.getBean("foo");
1.首先例項化一個容器物件,
2.然後由ResourceLoader元件對引數"applicationContext.xml"進行解析,將此路徑上指定的檔案解析為Resource物件。
3.BeanDefinitionReader將Resource資源物件內的bean元素資料封裝到BeanDefintion元件中,並通過BeanDefintionRegistry將BeanDefintion註冊到
BeanFactory中,
4.對Resource的解析工作主要包括三個主要部分,
a:對xml文件的schema驗證,
b:對預設名稱空間元素的解析,這部分委託給XmlBeanDefintionParser元件完成,
c:對客戶化名稱空間元素的解析,這部分工作委託給BeanDefintionParser完成,NamespaceHadler元件和NamespaceHandlerResolver元件對BeanDefintionParser提供支援工作。
這裡需要提到的是一些特殊元素的解析如:import元素的解析;另外一點是對applicationContext.xml檔案中的bean元素的實際解析工作是委託給
XmlBeanDefintionParserHelper類完成的;上面提到的元件介面列表中與BeanDefinition相關的元件有BeanDefinition元件,BeanDefintionRegistry介面,
除了這兩個最重要的,還有如:BeanDefintionBuilder,BeanDefintionDecorator,BeanDefinitionValueResolver,BeanDefinitionRegistryBuilder等其它與BeanDefintion相關元件,
都對BeanDefintion的操作提供支援。
至此,已經完成了階段性工作,就是已經將型別資訊從applicationContext.xml配置檔案bean元素中讀取到記憶體物件的BeanDefinition元件中,接下來的工作就是如何將
BeanDefintion元件中所儲存的型別資訊例項化為最終的物件。
5.接下來是容器的初始化工作:
呼叫BeanFactoryPostProcessor介面,
註冊BeanPostProcessor介面,
初始化MessageSource元件,
初始化ApplicationEventMulticaster,
註冊容器級監聽器,
釋出容器已重新整理的事件,
ApplicationContext介面對bean物件的初始化採取一種積極初始化策略,這樣做容器初始化過程雖然比較慢,但後續的每一次bean訪問相對較快,因為可以從singletonCache快取中直接獲取,
6.
至此下面這行程式碼的執行過程已結束,
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
接下來看這行程式碼的執行過程,
Foo foo = (Foo)applicationContext.getBean("foo");
7.
getBean(String)的目標很明確,就是根據bean的名稱得到一個bean物件,
對bean物件的不同角度的分類,
首先可以分為普通的非FactoryBean型別的bean物件和FactoryBean型別的bean物件,
其次可以分為singleton型別的bean物件和非singleton型別的bean物件,
但是這些分類是建立在一個已建立的bean物件基礎之上。
8.下面來看建立一個bean物件的過程,
createBean();
首先容器對BeanDefintion進行整理,根據依賴、繼承關係進行合併以得到最終的BeanDefintion,
然後進行bean物件的例項化、初始化、對需要銷燬操作的bean物件進行註冊。
在這一過程中涉及到一組回撥介面的呼叫,包括例項化前後的處理邏輯,初始化前後的處理邏輯,初始化過程的回撥邏輯,銷燬操作執行邏輯,
主要的回撥介面有
InitializingBean
DisposableBean
BeanPostProcessor
XXXXXXAware
配置風格的回撥機制(init-method,destroy-method)
對bean物件的初始化工作依賴於BeanWrapper元件,BeanWrapper元件以反射的方式將BeanDefintion元件中儲存的屬性資訊設定到bean物件中。
元件描述:
Resource元件與ResourceLoader元件一起工作,將字串格式指示的資源解析為Resource物件。
事實上ResourceLoader是Resource的工廠類,
- public interface ResourceLoader {
- public Resource getResource(String location);
- }
ResourceLoader的核心工作就是解析location,
location示例:"classpath:applicationContext.xml","classpath*:applicationContext-*.xml","file:/some/resource/path/myTemplate.txt","http://myhost.com/resource/path/myTemplate.txt"
ResourceLoader根據所指示的字首返回特定的Resource物件。
BeanDefintionReader元件,
- //將Resource中的內容通過BeanDefintionRegistry註冊到BeanFactory中。
- public interface BeanDefintionReader {
- BeanDefinitionRegistry getBeanFactory();
- ResourceLoader getResourceLoader();
- int loadBeanDefinitions(Resource[] resources) throws BeanDefinitionStoreException;
- int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
- int loadBeanDefinitions(String[] locations) throws BeanDefinitionStoreException;
- /*
- *這是一個基礎方法,從Resource中載入BeanDefinition;
- *這三個方法loadBeanDefintions(Resources[]),loadBeanDefintions(String[]),loadBeanDefintion(String)的實現
- *依賴於此方法的實現;
- *上面三個方法的實現在AbstractBeanDefinitionReader骨架類中完成,此方法的實現在XmlBeanDefintionReader中完成。
- */
- int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
- }
- //XmlBeanDefinitionParser元件處理配置檔案中預設名稱空間的元素解析,
- public interface XmlBeanDefinitionParser {
- //對Document文件的解析,將解析出的內容封裝到BeanDefintion中。
- void registerBeanDefinitions(Document doc, ReaderContext readerContext)throws BeanDefinitionStoreException;
- }
這三個元件(BeanDefintionParser元件,NamespaceHandler元件,NamespaceHandlerResolver元件)處理客戶化的名稱空間元素的解析,
此機制使用配置檔案易於書寫,具有可擴充套件性。
如spring提供的實現:<util:list>,<aop:config>,<tx:annotation-driven>,<context:annotation-config>,
第三方元件提供的實現:<jaxws:endpoint>,<amq:broker>等其它實現。
- //對客戶化名稱空間的bean元素進行解析操作。
- public interface BeanDefintionParser {
- //對Element的解析。
- BeanDefinition parse(Element element, ParserContext parserContext);
- }
- //根據元素名稱空間得到此元素的BeanDefinitionParser處理程式;此類是BeanDefintionParser的工廠類。
- public interface NamespaceHandler {
- void init();
- BeanDefinitionParser findParserForElement(Element element);
- BeanDefinitionDecorator findDecoratorForElement(Element element);
- }
- //解析META-INF/spring.handlers中的配置資訊;此類是NamespaceHandler 的工廠,
- public interface NamespaceHandlerResolver {
- //根據名稱空間指示符得到指定的名稱空間處理器。
- NamespaceHandler resolve(String namespaceUri);
- }
BeanWrapper元件,
對java bean物件提供設定屬性值、獲取屬性值操作,並能夠將字串型別值轉換為正確的型別,這個工作依賴於PropertyEditor。
操作示例:
beanWrapper.setPropertyValue("name","foo");
beanWrapper.setPropertyValue("address.country","China");
beanWrapper.setPropertyValue("array[2]","arrayValue");
- //PropertyEditor註冊器;提供註冊、獲取PropertyEditor操用。
- public interface PropertyEditorRegistry {
- void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor);
- void registerCustomEditor(Class requiredType, String propertyPath, PropertyEditor propertyEditor);
- PropertyEditor findCustomEditor(Class requiredType, String propertyPath);
- }
- //bean物件的屬性訪問器。
- public interface PropertyAccessor {
- public boolean isReadableProperty(String name);
- public boolean isWritableProperty(String name);
- public Class<?> getPropertyType(String name);
- public Object getPropertyValue(String name);
- public void setPropertyValues(PropertyValues pvs);
- public void setPropertyValues(Map<String,Object> pvs);
- public void setPropertyValue(PropertyValue pv);
- public void setPropertyValue(String name,Object value);
- }
- //對PropertyEditor增加管理功能。
- public interface ConfigurablePropertyAccessor extends PropertyEditorRegistry,PropertyAccessor {
- void setExtractOldValueForEditor(boolean extractOldValueForEditor);
- boolean isExtractOldValueForEditor();
- }
- //對bean物件進行管理。
- public interface BeanWrapper extends ConfigurablePropertyAccessor {
- //設定所在包裝的object
- void setWrappedInstance(Object obj);
- //返回包裝物件.
- Object getWrappedInstance();
- //返回包裝物件型別
- Class getWrappedClass();
- //返回包裝物件屬性描述..
- PropertyDescriptor[] getPropertyDescriptors() throws BeansException;
- //根據屬性名返回特定的屬性描述物件.
- PropertyDescriptor getPropertyDescriptor(String propertyName) throws BeansException;
- }
- //IoC容器的核心介面,提供訪問IoC容器的基本操作。
- public interface BeanFactory {
- //根據bean名稱獲取相應的bean物件. 此方法在AbstractBeanFactory骨架類中實現,
- public Object getBean(String name) throws BeansException;
- }
- //定義分層的BeanFactory容器結構。
- public interface HierarchicalBeanFactory extends BeanFactory {
- }
- //對BeanFactory提供配置資訊.
- public interface ConfigurableBeanFactory extends HierarchicalBeanFactory {
- void setParentBeanFactory(BeanFactory parentBeanFactory);
- // 註冊客戶化屬性編輯器.
- void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor);
- //新增BeanPostProcessor.
- void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
- //銷燬所有singleton型別bean物件.
- void destroySingletons();
- }
- //主要邏輯有建立一個bean物件例項的過程,根據不同的WireMode(byName、byType)完成不同的操作。
- public interface AutowireCapableBeanFactory extends BeanFactory {
- //建立一個bean物件.
- Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
- //配置一個bean物件.
- Object configureBean(Object existingBean, String beanName) throws BeansException;
- //初始化bean物件.
- Object initializeBean(Object existingBean, String beanName) throws BeansException;
- Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException;
- Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException;
- }
- /*
- *提供對BeanDefintion物件的管理操作。
- *對通過BeanDefintionRegistry註冊器註冊到BeanFactory中的BeanDefintion物件進行管理,但注意此介面
- *並不依賴於BeanDefintion介面API;
- *此介面在操作上與BeanDefintionRegistry介面有重疊部分,但此介面的職責重在管理操作,而BeanDefintionRegistry重在註冊操作,並且
- *BeanDefintionRegistry介面直接依賴於BeanDefintion介面API。
- */
- public interface ListableBeanFactory {
- boolean containsBeanDefinition(String beanName);
- int getBeanDefinitionCount();
- String[] getBeanDefinitionNames();
- String[] getBeanNamesForType(Class type);
- String[] getBeanNamesForType(Class type, boolean includePrototypes, boolean includeFactoryBeans);
- Map getBeansOfType(Class type) throws BeansException;
- Map getBeansOfType(Class type, boolean includePrototypes, boolean includeFactoryBeans) throws BeansException;
- }
- //
- public interface ConfigurableListableBeanFactory
- extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory,SingletonBeanRegistry {
- void ignoreDependencyType(Class type);
- void ignoreDependencyInterface(Class ifc);
- BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
- //在容器啟動過程中例項化singleton型別bean物件。
- void preInstantiateSingletons() throws BeansException;
- }
- //註冊BeanDefintion物件,並進行管理操作。
- public interface BeanDefinitionRegistry {
- int getBeanDefinitionCount();
- String[] getBeanDefinitionNames();
- boolean containsBeanDefinition(String beanName);
- BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
- void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeansException;
- String[] getAliases(String beanName) throws NoSuchBeanDefinitionException;
- void registerAlias(String beanName, String alias) throws BeansException;
- }
- /*
- *註冊singleton型別物件,並進行管理操作;BeanDefinitionRegistry介面將BeanDefinition註冊到BeanFactory中,此介面將singleton型別
- *物件註冊到BeanFactory中。
- */
- public interface SingletonBeanRegistry {
- }
ApplicationEventPublisher介面是容器事件釋出介面,
ApplicationEventPublisher介面的功能是委託給ApplicationEventMulticaster元件實現的,
ApplicationEventMulticaster元件提供對監聽器的完整操作,包括新增監聽器、移除單個或全部監聽器、通知監聽器。
- //容器事件釋出器。
- public interface ApplicationEventPublisher {
- void publishEvent(ApplicationEvent event);
- }
- //一個完整的事件模型實現。
- public interface ApplicationEventMulticaster {
- void addApplicationListener(ApplicationListener listener);
- void removeApplicationListener(ApplicationListener listener);
- void removeAllListeners();
- /*
- *事件釋出方法,通知所有監聽器;
- *ApplicationEventPublisher.publishEvent(ApplicationEvent)方法的實現委託給此方法完成。
- */
- void multicastEvent(ApplicationEvent event);
- }
- //監聽器.
- public interface ApplicationListener extends java.util.EventListener {
- }
- //事件物件.
- public class extends ApplicationEvent extends java.util.EventObject {
- }
MessageSource元件
MessageSourceResolvable元件
這是一個介面複用與組合複用協同工作的好例子,ApplicationContext介面繼承了MessageSource介面,對外提供資訊源處理操作,但內部實現委託給MessageSource元件完成。
- //SpringIoC容器的頂級介面.
- public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory,
- MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
- ApplicationContext getParent();
- AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
- String getDisplayName();
- long getStartupDate();
- }
- //對容器物件進行配置化、初始化工作.
- public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle {
- void setParent(ApplicationContext parent);
- void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor);
- //此方法是核心方法,內部生成一個BeanFactory物件,並完成對BeanFactory物件的初始化和容器的初始化工作。
- void refresh() throws BeansException, IllegalStateException;
- ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
- void close();
- }
- //生命週期管理介面.
- public interface Lifecycle {
- void start();
- void stop();
- boolean isRunning();
- }
WebApplicationContext
ConfigurableWebApplicationContext
AbstractRefreshWebApplicationContext
XmlWebApplicationContext
- //提供SpringIoC容器對Web環境ServletAPI的整合。
- public interface WebApplicationContext extends ApplicationContext {
- ServletContext getServletContext();
- }
- //提供WebApplicationContext的配置工作。
- public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext {
- void setServletContext(ServletContext servletContext);
- void setServletConfig(ServletConfig servletConfig);
- void setNamespace(String namespace);
- void setConfigLocations(String[] configLocations);
- }
- //ContextLoader元件從ServletContext初始化配置引數中獲取Spring的配置檔案路徑資訊,並進行IoC容器的例項化、初始化、銷燬操作。
- public class ContextLoader {
- public WebApplicationContext initWebApplicationContext(ServletContext servletContext){
- //code.
- }
- public void closeWebApplicationContext(ServletContext servletContext) {
- //code.
- }
- }
ContextLoaderListener和ContextLoaderServlet提供兩種方式將IoC容器整合到ServletContext快取中。
從物件導向基本設計原則角度來看SpringIoC容器的設計:
最基本的兩條設計原則--程式設計到介面、首選組合複用:
程式設計到介面,
在SpringIoC容器實現中,interface 關鍵字隨處可見,但是有一點需要注意的就是:並不是使用了interface關鍵字,就能保證程式設計到介面,
但一般來說對程式設計到介面原則的遵守,inteface關鍵字的使用是必須的。程式設計到介面所描述的實質是要將元件的外部介面和內部實現分離
開來,這將帶來一系列的好處:可擴充套件性,可重用性,可維護性,依賴性,內聚性,耦合性,清晰性,簡潔性,可讀性,可修改性,
抽象性,封裝性,模組化,層次化,測試性,其它特性。
首選組合複用,
物件導向的複用方式主要分兩種--組合複用、繼承複用,繼承複用可以細分為兩種--介面複用、具體複用,
這條原則關注的組合複用與具體複用之間的區別,
事實上這條原則針對複用方式的選擇上意義並不大,因為這三種複用方式所處理的是不同的複用問題,一旦能夠從has-a、is-like-a、is-a的角度
區分開,問題就不大了。
這條原則真正有意義的在於它的教訓意義,可以藉此瞭解這條原則形成的原因,全面瞭解複用的方式,瞭解每一種複用方式的特點,瞭解不同
複用方式之間的差別。
SpringIoC容器的設計很好的體現了這兩條原則,如ApplicationContext介面
- public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory,
- ssageSource, ApplicationEventPublisher, ResourcePatternResolver {
- }
這是一個基於介面的設計,並且是一個介面複用,
ApplicationContext介面繼承了ListableBeanFactory,HierarchicalBeanFactory,MessageSource,ApplicationEventPublisher,ResourcePatternResolver
介面,那麼就意味著 ApplicationContext可以提供這些介面中定義的所有功能,但是這些功能的實際實現並不是由ApplicationContext的實現類
提供的,而是以組合複用的方式委託給了各個介面的實際實現類來完成;
具體複用在介面的實現過程中所使用,以便將介面的設計層次與介面的實現層次分離開來,如:
- public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
- }
更為全面的設計原則描述:
單一職責、開閉原則、里氏代換原則、依賴倒轉原則、介面隔離原則、組合複用原則、迪米特法則;
共同重用原則、共同封閉原則,無環依賴原則、穩定依賴原則、穩定抽象原則,
總結:
要全面理解IOC容器,回答下述問題是必須的。
1。IOC容器是什麼,
2。IOC容器提供什麼樣的功能,
3。IOC容器的特徵是什麼,
4。IOC容器設計的理論依據是什麼,
5。IOC容器的設計需要注意的問題是什麼,
6。如何實現一個IOC容器,
7。不同容器、不同IOC容器之間的比較,
參考目錄:
《Expert One-on-One J2EE Design and Development》
《Expert One-on-One J2EE Development without EJB》
《Professional Java Development with the Spring Framework》
《Effective Java》
《Refactoring-Improving the Design of Existing Code》
《Agile Software Development Principles,Patterns,and Practices》
《Code Complete II》