背景
在企业开发框架很多,如: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 | public class MyTestBean { |
现在采用XmlBeanFactory获取bean:
1 | BeanFactory bf=new XmlBeanFactory(new ClassPathResource("beans.xml")); |
上面的方法简单的展示了bean的加载,解析和获取的过程.该流程可以概括如下:
- 将beans.xml文件看做一个资源,并加载该资源
- 从加载后的资源中解析bean和bean的依赖关系获得bean定义
- 将bean定义存入注册工厂中
- 根据bean的名字从注册工厂中获取bean实例
针对这个流程我们具体分析下整个IoC的框架设计.
XmlBeanFactory类
XmlBeanFactory是我们IoC源码分析的切入口,所以我们首先看看XmlBeanFactory的具体代码,就可以知道整个框架的大致设计.
1 | public class XmlBeanFactory extends DefaultListableBeanFactory { |
从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的工作流程.它的大致流程如下:
- 使用ResourceLoader加载指定路径的资源文件.
- 使用DocumentReader,将资源文件转化为Document.
- 利用BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader类对Document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析得到BeanDefinition.
资源文件
spring对器内部使用到的资源,实现了自己的抽象结构.将所有的使用到的资源当做Resource对待,Resource抽象了所有的底层资源如:File,URL,Classpath等.这样的结构使得spring可以对所有资源文件的操作统一处理,也使得资源与操作之间的解耦,做到面向接口开发.
参考资料
-《spring源码深度解析》
- spring源码