Spring源码学习--IoC容器设计原理

背景

在企业开发框架很多,如:struts2+hibernate+spring,springMVC+hibernate+spring,  springMVC+MyBatis+spring等,但是不管采用哪种框架组合的配置总是少不了spring的身影.我们可以用struts2 或springMVC,数据访问可以hibernate,jdbc或MyBatis,这些都可以替换.但是spring不管在哪种组合中都可以出现,这是因为spring可以简化java开发,使应用做到高内聚,松耦合.当然还有它提供的很多优秀的功能,如AOP,事务,和其他框架完美融合等等.
既然spring是一个优秀的框架,它之所受大家的推崇和喜爱,不仅是它的功能还有它的框架设计和源码.它的框架设计精妙,结构清晰,处处体现了大师们对java设计模式的灵活应用以及对java技术的高深造诣.spring框架无疑时java技术的最佳实践范例,而且很多公司在招聘要求上或多多少提到spring源码的阅读经历.所以不管是哪个层次的java开发人员,spring源码都会给我们带来意想不到的收获.
鉴于以上原因,从图书馆借来一本 spring源码深度解析 ,根据书中的思路开始spring源码的探险之旅.这本书主要是根据分析spring某些功能的逻辑,主要集中于带你如何去看spring源码,所以比自己通过debug的方式看开源代码会省心很多,节省不少时间.毕竟受版面的约束,这本书对于有些细节并没有深挖,这些都需要靠自己去分析学习.
学习spring源码像作者说的一样,就像剥洋葱,一层一层的去看代码绕来绕去最后不知道自己在哪,以至于让你有种鼻酸想哭的冲动.即便这个过程是辛苦的,费时间的,但是每一次阅读都会带给你巨大的收获.目前只看完第六章,把spring容器和容器的功能拓展看完了,初步了解了整个容器的逻辑过程,但对于细节即内涵的丰富的设计模式还是没有太多的领悟,所以还需要再次的细细品味,领会大师们的设计精髓.因为准备再次将这部分深入学习,并做好这块的笔记.

IoC的概念及设计思想

IoC概念

控制反转(Inverse of Control,IoC) 是spring容器的内核,AOP,声明式事务等功能都是在此基础上开花结果的.所谓IoC,就是通过容器来控制业务对象之间的依赖关系,而非传统实现中,由代码直接操作. 这也是控制反转的概念所在:控制权有应用代码中转到外部容器,控制权的转移就是反转.控制权的转移带来的好处就是降低了业务对象之间的依赖程序.

BeanFactory和ApplicationContext

为了完成控制反转,spring通过读取一个描述Bean和Bean的依赖关系的配置文件,并进行解析得到bean定义(BeanDefinition),并将这些注册到容器中.并利用java反射功能实例化bean并建立bean之间的依赖关系.spring的Ioc容器在完成这些底层工作的基础上还提供了bean实例缓冲,生命周期管理,事件发布等高级服务.
Bean工厂(BeanFactory)是Spring框架最核心的接口,提供了高级Ioc的配置机制.应用上下文(ApplicationContext)建立在BeanFacotry基础之上,提供了更多面向应用的功能,如果国际化,属性编辑器,事件等等.beanFactory是spring框架的基础设施,是面向spring本身,ApplicationContext是面向使用Spring框架的开发者,几乎所有场合都会用到ApplicationContext.

设计思路

根据IoC的概念我们知道要完成该功能,Spring必须能够从配置文件或者类中读取和解析描述Bean和Bean的依赖关系并得到Bean的定义.将这些定义保存到容器中,当需要的时候可以根据bean的名字获取对应的Bean实例.因为单例模式只实例化一次,所以在容器中还应该提供一个单例缓冲,保存已经实例化的bean.
所以根据这个思路,spring中容器定义了几个重要的接口:

  • BeanDefinitionRegistry接口:用于注册解析后的bean定义
  • SingletonBeanRegistry接口:单例注册器,用于缓冲和获取单例.
  • BeanFactory接口:核心接口,用于获取Bean及bean的各种属性
  • BeanDefinitionReader接口: 用于读取和解析bean和bean的依赖关系

前三个接口是容器必备的,而最后一个接口是为了寻找bean并注入到容器中.DefaultListableBeanFactory 是整个bean加载的核心部分,包含了前三个接口的全部实现.所以它可以说一个spring容器,可以从中注册和获取bean.因为bean和bean的依赖关系定义有很多种,所以对于bean和bean依赖关系的加载方式也有很多. 本文是针对于配置文件的加载,这里使用XmlBeanFactory来说明整个容器的加载,解析和获取过程.XmlBeanFactory继承了DefaultListableBeanFactory,同时为了支持Xml文件的加载和读取,XmlBeanFactory还包括了一个重要的类XmlBeanDefinitionReader(是BeanDefinitionReader接口的一个个性化实现).
XmlBeanDefinitionReader主要是读取xml配置文件,解析bean,并解析后的bean注册到工厂中.所以XmlBeanDefinitionReader依赖于一个注册工厂,将解析后的bean注册到注册工厂中.这是IoC框架的大致流程,和一些框架设计.当然内部细节设计很多很多,这里只是粗略了概括整个设计.具体细节我们将在下面慢慢的解析.

框架设计

例子

首先我们回顾下spring中bean的获取过程,平时开发一般会采用ApplicationContext上下文来加载配置文件和获取bean,这里我们用另一个方式来了解spring基层的运作机制.所以这里我们用XmlBeanFactrory 来加载和解析bean.首先我们定义一个bean类,并在配置文件中定义该bean和依赖关系:

1
2
3
4
5
6
7
8
9
10
11
12
public class MyTestBean {
private String testStr="testStr";

public String getTestStr(){
return testStr;
}
public void setTestStr(String testStr){
this.testStr=testStr;
}
}

<bean id="myTestBean" class="learn.MyTestBean"></bean>

现在采用XmlBeanFactory获取bean:

1
2
BeanFactory bf=new XmlBeanFactory(new ClassPathResource("beans.xml"));
MyTestBean bean=(MyTestBean)bf.getBean("myTestBean");

上面的方法简单的展示了bean的加载,解析和获取的过程.该流程可以概括如下:

  • 将beans.xml文件看做一个资源,并加载该资源
  • 从加载后的资源中解析bean和bean的依赖关系获得bean定义
  • 将bean定义存入注册工厂中
  • 根据bean的名字从注册工厂中获取bean实例
    针对这个流程我们具体分析下整个IoC的框架设计.

XmlBeanFactory类

XmlBeanFactory是我们IoC源码分析的切入口,所以我们首先看看XmlBeanFactory的具体代码,就可以知道整个框架的大致设计.

1
2
3
4
5
6
7
8
9
10
11
12
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
throws BeansException {
super(parentBeanFactory);
//整个资源加载的切入点
this.reader.loadBeanDefinitions(resource);
}
}

从XmlBeanFactory的源码可以清晰的看出,它是一个继承DefaultListableBeanFactory类的工厂,同时为了支持xml的读取,需要一个XmlBeanDefinitionReader的解析器.该解析器初始化时需要一个注册工厂用于将解析后的bean定义(BeanDefinition)注册到工厂中.

DefaultListableBeanFactory类

下面是DefaultListableBeanFactory类关系图:

  • AliasRegistry: 定义了对别名alias的简单增删改等操作.spring中可以为每个bean注册不同的别名.
  • SimpleAliasRegistry: 是对AliasRegistry的简单实现,内部采用map作为别名的缓冲和映射.
  • BeanFactory: 定义获取bean及bean的各种属性
  • SingletonBeanRegistry: 定义单例的注册及获取.
  • DefaultSingletonBeanRegistry : 定义对SingletonBeanFactory的实现.
  • HierachicalBeanFactory : 是一个层次的Bean工厂,它继承BeanFactory,并增加了对parentFactory的支持.
  • BeanDefinitionRegistry: 负责对BeanDefinition的增删改,BeanDefinition表示bean的定义.
  • FactoryBeanRegistrySupport: 在DefaultSingletionRegistry基础上增加了对FactoryBean的特殊处理.
  • ListableBeanFactory: 根据各种条件获取bean的配置清单.
  • AbstractBeanFactory: 综合FactoryBeanRegistrySupport 和 ConfigurableBeanFactory的功能.
  • AbstractAutowireCapableBeanFactory: 综合AbstractBeanFactory并对接口AutowireCapableBeanFactory进行实现.
  • ConfigurableListableBeanFactory: BeanFactory配置清单,指定忽略类型及接口等.
  • DefaultListableBeanFactory: 综合上面的所有功能,主要是对Bean注册后的处理.

XmlBeanDefinitionReader

XmlBeanDefinitionReader主要时负责读取XML配置文件中定义的Bean,并注册到容器中.因为spring中的很多配置都是XML配置的,所以XmlBeanDefinitionReader在spring内部起着关键的作用.

首先我们看下整个XmlBeanDefinitinReader的类关系结构,如下图所示:

  • ResourceLoader: 定义资源加载器,在spring中将所有文件等都看做资源,如将配置文件xml当做资源来看待.资源加载器根据指定路径加载和返回对应资源.
  • BeanDefinitionReader: 前面我们说过spring将bean的配置或定义用BeanDefinition类表示,所以该类主要负责从资源文件读取bean的定义并转化为BeanDefinition.
  • EnviromentCapable: 定义获取Enviroment方法.
  • DocumentLoader: 加载资源文件并转化为Document.
  • AbstractBeanDefinitionReader: 实现了Enviroment,BeanDefinitionReaer接口
  • BeanDefinitionDocumentReader: 定义读取Document并注册BeanDefinition功能.
  • BeanDefinitionParserDelegate: 定义解析Element的各种方法.

根据上图,我们来讲解下XmlBeanDefinitionReader的工作流程.它的大致流程如下:

  1. 使用ResourceLoader加载指定路径的资源文件.
  2. 使用DocumentReader,将资源文件转化为Document.
  3. 利用BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader类对Document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析得到BeanDefinition.

资源文件

spring对器内部使用到的资源,实现了自己的抽象结构.将所有的使用到的资源当做Resource对待,Resource抽象了所有的底层资源如:File,URL,Classpath等.这样的结构使得spring可以对所有资源文件的操作统一处理,也使得资源与操作之间的解耦,做到面向接口开发.

参考资料

-《spring源码深度解析》

  • spring源码
0%