spring ioc源码学习

 

转自 http://www.iteye.com/magazines/72

 

 

本文主要以 spring ioc 容器基本代码骨架为切入点,理解 ioc 容器的基本代码组件结构,各代码组件细节剖析将放在后面的学习文章里。

 

关于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 ioc源码学习_第1张图片

 

 

具体可以参考文章 :

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 对象。


spring ioc源码学习_第2张图片
 

4.BeanFactory

用来定义一个很纯粹的 bean 容器。它是一个 bean 容器的必备结构。同时和外部应用环境等隔离。 BeanDefinition 是它的基本数据结构。它维护一个 BeanDefinitions Map, 并可根据 BeanDefinition 的描述进行 bean 的创建和管理。


spring ioc源码学习_第3张图片
 

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 之间提供了事件机制。

……


spring ioc源码学习_第4张图片
 

 

以上 5 个组件基本代表了 ioc 容器的一个最基本组成,而组件的组合是放在 ApplicationContext 的实现这一层来完成。

 

以ClasspathXmlApplicationContext 容器实现为例,其组合关系如下:

 


spring ioc源码学习_第5张图片

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()代码如下:

Java代码
  1. public   void  refresh()  throws  BeansException, IllegalStateException {  
  2.     synchronized  ( this .startupShutdownMonitor) {  
  3.         // Prepare this context for refreshing.   
  4.         prepareRefresh();  
  5.   
  6.         // Tell the subclass to refresh the internal bean factory.   
  7.         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
  8.   
  9.         // Prepare the bean factory for use in this context.   
  10.         prepareBeanFactory(beanFactory);  
  11.   
  12.         try  {  
  13.             // Allows post-processing of the bean factory in context subclasses.   
  14.             postProcessBeanFactory(beanFactory);  
  15.   
  16.             // Invoke factory processors registered as beans in the context.   
  17.             invokeBeanFactoryPostProcessors(beanFactory);  
  18.   
  19.             // Register bean processors that intercept bean creation.   
  20.             registerBeanPostProcessors(beanFactory);  
  21.   
  22.             // Initialize message source for this context.   
  23.             initMessageSource();  
  24.   
  25.             // Initialize event multicaster for this context.   
  26.             initApplicationEventMulticaster();  
  27.   
  28.             // Initialize other special beans in specific context subclasses.   
  29.             onRefresh();  
  30.   
  31.             // Check for listener beans and register them.   
  32.             registerListeners();  
  33.   
  34.             // Instantiate all remaining (non-lazy-init) singletons.   
  35.             finishBeanFactoryInitialization(beanFactory);  
  36.   
  37.             // Last step: publish corresponding event.   
  38.             finishRefresh();  
  39.         }  
  40.   
  41.         catch  (BeansException ex) {  
  42.             // Destroy already created singletons to avoid dangling resources.   
  43.             beanFactory.destroySingletons();  
  44.   
  45.             // Reset 'active' flag.   
  46.             cancelRefresh(ex);  
  47.   
  48.             // Propagate exception to caller.   
  49.             throw  ex;  
  50.         }  
  51.     }  
  52. }  

 以上的obtainFreshBeanFactory是很关键的一个方法,里面会调用loadBeanDefinition方法,如下:

Java代码
  1. protected   void  loadBeanDefinitions(DefaultListableBeanFactory beanFactory)  throws  IOException {  
  2.     // Create a new XmlBeanDefinitionReader for the given BeanFactory.   
  3.     XmlBeanDefinitionReader beanDefinitionReader = new  XmlBeanDefinitionReader(beanFactory);  
  4.   
  5.     // Configure the bean definition reader with this context's   
  6.     // resource loading environment.   
  7.     beanDefinitionReader.setResourceLoader(this );  
  8.     beanDefinitionReader.setEntityResolver(new  ResourceEntityResolver( this ));  
  9.   
  10.     // Allow a subclass to provide custom initialization of the reader,   
  11.     // then proceed with actually loading the bean definitions.   
  12.     initBeanDefinitionReader(beanDefinitionReader);  
  13.     loadBeanDefinitions(beanDefinitionReader);  
  14. }  

 LoadBeanDifinition方法很关键,这里特定于整个IOC容器,实例化了一个XmlBeanDefinitionReader来解析Resource文件。关于Resource文件如何初始化和xml文件如何解析都在

Java代码
  1. loadBeanDefinitions(beanDefinitionReader);  

 里面的层层调用完成,这里不在累述。

小结 

Spring 的扩展性是毋庸置疑的,学习 spring 的设计是一个很好的实践理论结合。主要个人觉得有几点:

1.  框架顶层的设计有着很好的抽象,遵循面向接口编程的规范。 Resource BeanFactory ApplicationContext 都是非常好的接口抽象,非常明确的定义了该组件的一些功能。

2.  利用组合模式。

3.  个组件的实现里大量使用了模板方法模式,提升了同一组件代码的复用性。

4.  各种设计保留了扩展的接口,很多基于 spring 的框架都可以很容易的介入实现了自己的一些扩展。

5.  框架里采用里很多经典的设计模式,如代理、装饰、策略等等。

你可能感兴趣的:(spring,IOC)