Spring2.0中的IOC漫笔(一)之bean容器

Spring2.0中的IOC随笔(一)之bean容器
        最近在看Spring2.0中的IOC,索性翻开源码一探究竟。Spring提供的轻量级bean容器其核心由两个概念组成,一个是bean容器,另一个是bean工厂(当然还有很多其他复杂的组件来支持他们,在此不是我们此次的关注重点)。或者说得形象一点就如同一个是库房,另一个是供应商,这两部分紧密相关,相互提供服务。首先我们看看bean容器,在利用Spring做Web应用的时候,我们大都会用到的Spring提供的bean容器XmlWebApplicationContext,所有你在配置文件(例如applicationContext.xml)中配置的bean都是由他来负责管理,因此我们就从他开始搜寻线索。如下图所示为XmlWebApplicationContext的继承体系,千万不要被他的复杂所吓倒,因为一旦你熟悉了这些接口或者类所负责的作用,提供的服务,明白了Spring为什么这样划分,组织这些功能,以及如何严丝合缝的将这些不同方面的功能通过抽象,接口以及继承等OO手段组织起来以后,再回过头来看Spring。就可以如同庖丁解牛一般地看待他,就会在日后的开发中更加合理地利用Spring为我们提供的便捷的服务。
Spring2.0中的IOC漫笔(一)之bean容器

        乍一看这张图,可能会觉得无从下手,那我们就一点一点地来剖析,最后再进行整合。首先我们来看最上面的三个接口,如下图所示:
Spring2.0中的IOC漫笔(一)之bean容器

        接口BeanFactory顾名思义是一个bean工厂,而且是所有bean工厂最顶层的接口,其中定义了获得单个bean实例及其类型的协议(文章暂且将接口中的方法视为协议)以及判断该bean是否为单体模式。
        接着看下面两个子接口,不知道你注意到没有,这两个子接口的名字前都有一个修饰词,一个是Listable,另一个是Hierarchical。如果你看看Spring其他功能模块的代码就会发现更多类似的修饰词,如AbstractXXX,ConfigurableXXX,DefaultXXX,SimpleXXX等,这些修饰词都是在Spring各模块的不同继承层次中使用到,其中Listable表示该接口或者类提供了批量/集合操作的服务,就像接口ListableBeanFactory一样,定义了获得BeanDefinition(bean定义)数组以及某类Bean实例数组的协议;而Hierarchical使得所修饰的接口或者类具有了树形的继承体系结构,并定义了访问其父节点的服务。
        从此我们得出结论,ListableBeanFactory就是一个具有批量/单独操作bean的bean工厂,而HierarchicalBeanFactory就是一个具有树形结构的bean工厂。
我们再将视角放大一些,如下图所示:
Spring2.0中的IOC漫笔(一)之bean容器

        这次又多添加了两个子接口,一个为ApplicationContext,该接口除了多添加了一些服务外,其主要的作用是将其继承的两个接口做一个整合,使其成为一个既具备批量/单独操作其中bean能力的bean工厂,有具有了树形的体系结构;另一个子接口为ConfigurableApplicationContext,这是文章中第一次出现Configurable的接口,其含义为所修饰的接口或类向外提供了设置某些参数或者变量的服务。此处ConfigurableApplicationContext接口具有了可以设置其父工厂,添加bean工厂的后续处理器的功能,另外所添加的一个重要方法是refresh,该方法负责启动整个bean容器,包括配置文件的读取,解析,BeanDefinition的生成,以及单体模式Bean的组装和实例化等等一些列工作,是整个Bean容器的核心方法之一,正是由于该方法做了很多具体的工作因此内部使用了大量的模板方法(method template设计模式),具体的工作都是在具体的bean容器中实现,比如Web开发中applicationContext.xml配置文件的读取是在XmlWebApplicationContext中实现。
再看下一幅图:
Spring2.0中的IOC漫笔(一)之bean容器

        有三个新成员加入,分别是ResourceLoader,DefaultResourceLoader和AbstractApplicationContext。ResourceLoader是一个接口,定义了获取资源的协议;DefaultResourceLoader是ResourceLoader的默认实现,其中保存了一个ClassLoader的实例变量,并实现了核心的getResouce方法;而AbstractApplicationContext实现了接口ConfigurableApplicationContext中的大部分方法,仅留出了几个抽象的模板方法供具体Bean容器去实现,同时AbstractApplicationContext实现的另一个分支DefaultResourceLoader目的是使得Bean容器具有加载资源的能力,例如对于XmlWebApplicationContext这个Web开发时的Bean容器,DefaultResourceLoader就使其具有了加载XML配置文件的能力。
至此,Bean容器AbstractApplicationContext具有的功能已有:
1) 操作单个Bean
2) 操作多个Bean/BeanDefinition
3) 具有树形体系结构
4) 加载资源

        最后我们可以完整的看一下继承体系图了,如下所示:
Spring2.0中的IOC漫笔(一)之bean容器

        此次添加了最后三个类,分别是AbstractRefreshableApplicationContext,AbstractRefreshableWebApplicationContext和XmlWebApplicationContext。这三个类都是在其父类AbstractApplicationContext的基础上稍许增加了一些功能或者实现了父类中的一些模板方法。
        也许你已经注意到了,这是又出现了两个形容词,一个是refreshable,另一个是web。
因此你可以猜想AbstractRefreshableApplicationContext是不是在其父类AbstractApplicationContext的基础上添加了什么“刷新“的功能。恭喜你!答对了。That’s it. 一旦你熟悉了Spring中的命名规则以及对于功能和继承层次的组织习惯,就能很容易理解他的设计思路。言归正传,AbstractRefreshableApplicationContext的确实现了bean容器的刷新功能,具体方法为refreshBeanFactory,该方法是在父类的refresh()方法中调用的,具体包括1) 创建一个新的Bean工厂,并保存在其内部的实例变量beanFactory中;2) 重新加载配置文件中所有配置的bean。
        另一个词web所修饰的类AbstractRefreshableWebApplicationContext又是在其父类AbstractRefreshableApplicationContext基础上添加了web应用所特有的两个实例变量servletContext和servletConfig以及相应的一些辅助方法实现。
最后的主角闪亮登场了,他就是XmlWebApplicationContext。该bean容器在其父类的基础上定义了一些与Web应用和Xml配置文件相关的变量和方法,其中主要的有变量DEFAULT_CONFIG_LOCATION_PREFIX,该变量为XML配置文件的默认路径和名称,在其定义的时候被默认初始化为/WEB-INF/applicationContext.xml;另一个主要实现的方法为loadBeanDefinitions,该方法为定义在父类中的一个模板方法,之所以被定义为模板方法是因为该方法的实现依赖于具体的应用以及所选取配置文件方式。具体配置文件的加载和解析是通过类XmlBeanDefinitionReader来完成的,整个过程说来话长,这就涉及到另一个话题了,就是bean的装配过程。关于这一问题,我们下次再专题讨论。
        到此就完成了整个bean容器的分析。由此可以对Spring的设计思路窥见一斑:将不同的功能进行分离,隔离成不同的接口,不同的接口或者接口群负责一组相对独立的功能,不同功能的接口又可以相互组合成为一个接口,从而集成了多种功能,各种功能之间都是通过接口来相互调用,尽量不会涉及到具体的类。这就像我们搭积木一样,需要哪部分功能,就将相应的接口插上,不需要就拔下来,具有高度的可维护性和可扩展性。另外在继承体系中使用了大量的模板方法,将抽象的方法尽量的集中,而将具体的可变的方法通过模板方法的方式推迟到具体的类中去实现。