浅析Spring框架设计

来源:互联网 发布:怎么添加网络磁盘 编辑:程序博客网 时间:2024/06/04 18:13

一、Spring 的骨骼架构

浅析Spring框架设计
图 1 .Spring 框架的总体架构图
从上图中可看出Spring框架的核心组件有三个:Core、Context 和 Beans。它们构建起了整个 Spring 的骨骼架构。它们是 AOP、Web 等上层特性功能的基础。下面也将主要从这三个组件入手分析 Spring。
二、Spring 的设计理念
如果再从三个核心组件中选出核心,非 Beans 组件莫属了,Spring 就是面向 Bean 的编程(BOP,Bean Oriented Programming),Bean 在 Spring 中是真正的主角。
Spring 框架的设计目标:可把对象之间的依赖关系转而用配置文件来管理,也就是依赖注入机制。这个注入关系在 Ioc 容器中管理,那 Ioc 容器中储存和管理被 Bean 包裹的对象。Spring 通过把对象包装在 Bean 中来管理对象及额外对象操作。
这种设计策略是在构建一个数据结构,然后根据这个数据结构设计其生存环境,并让它在这个环境中按照一定的规律不停的运动,在它们的不停运动中设计一系列与环境或者与其他个体完成信息交换。其他框架都是大慨类似的设计理念。
三、核心组件如何协同工作
Bean 包装的是 Object,而 Object 必然有数据,Context给这些数据提供生存环境, Context 取得每个 Bean 之间的关系,为它们建立系并且维护好关系。所以 Context 是 Bean 关系的集合,这个关系集合又叫 Ioc 容器,一旦建立起这个 Ioc 容器后 Spring 就可工作了。 Core 组件是取得、建立和维护每个 Bean 之间的关系所需要的一系列工具,从这个角度看来,Core 这个组件也可叫 Util 。
它们之间可以用下图来表示:

浅析Spring框架设计
图 2. 三个组件关系
四、核心组件详解
这里介绍每个组件内部类的层次关系,及在运行时的时序顺序,使用 Spring 应该注意的地方。
1、Bean 组件
Bean 组件在 Spring 的 org.springframework.beans 包下。这个包下的所有类主要解决了三件事:Bean 的定义、Bean 的创建以及对 Bean 的解析。对 Spring 使用者唯一需要关心的就是 Bean 的创建,其他两个由 Spring 在内部完成。
(1)Spring Bean 的创建时典型的工厂模式,他的顶级接口是 BeanFactory,下图是这个工厂的继承层次关系:
浅析Spring框架设计
图 3. Bean 工厂的继承关系
BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。但是从上图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,他实现了所有的接口。那为何要定义这么多层次的接口呢?查阅这些接口的源码和说明发现,每个接口都有它使用的场合,它主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。例如 ListableBeanFactory 接口表示这些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个 Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。这四个接口共同定义了 Bean 的集合、Bean 之间的关系、以及 Bean 行为。
(2)Bean 的定义主要有 BeanDefinition 描述,如下图说明了这些类的层次关系:

浅析Spring框架设计
图 4. Bean 定义的类层次关系图
Bean 的定义就是完整的描述了在 Spring 的配置文件中你定义的 <bean/> 节点中所有的信息,包括各种子节点。当 Spring 成功解析你定义的一个 <bean/> 节点后,在 Spring 的内部他就被转化成 BeanDefinition 对象。以后所有的操作都是对这个对象完成的。
(3)Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成:

浅析Spring框架设计
图 5. Bean 的解析类
当然还有具体对 tag 的解析这里并没有列出。
2、Context 组件
Context 在 Spring 的 org.springframework.context 包下, Context 组件就是给 Spring 提供一个运行时的环境,用以保存各个对象的状态。下面看一下这个环境是如何构建的。
ApplicationContext 是 Context 的顶级父类,他除了能标识一个应用环境的基本信息外,他还继承了五个接口,这五个接口主要是扩展了 Context 的功能。下面是 Context 的类结构图:

浅析Spring框架设计

图 6. Context 相关的类结构图
从上图中可以看出 ApplicationContext 继承了 BeanFactory,说明 Spring 容器中运行的主体对象是 Bean,另外 ApplicationContext 继承了ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源。
ApplicationContext 的子类主要包含两个方面:
ConfigurableApplicationContext 表示该 Context 是可修改的,也就是在构建 Context 中用户可以动态添加或修改已有的配置信息,其下有多个子类,其中最经常使用的是可更新的 Context,即 AbstractRefreshableApplicationContext 类。
WebApplicationContext 是为 web 准备的 Context,可直接访问到 ServletContext,通常情况下,这个接口使用的少。
再往下分就是按照构建 Context 的文件类型,接着就是访问 Context 的方式。一级一级构成了完整的 Context 等级层次。
总体来说 ApplicationContext 必须要完成以下几件事:
(1)标识一个应用环境
(2)利用 BeanFactory 创建 Bean 对象
(3)保存对象关系表
(4)能够捕获各种事件
Context 作为 Spring 的 Ioc 容器,基本上整合了 Spring 的大部分功能,或者说是大部分功能的基础。
3、Core 组件
Core 组件作为 Spring 的核心组件,包含了很多的关键类,其中一个重要组成部分就是定义了资源的访问方式。这种把所有资源都抽象成一个接口的方式很值得在以后的设计中拿来学习。
下图是 Resource 相关的类结构图:

浅析Spring框架设计
图 7. Resource 相关的类结构图
从上图可以看出 Resource 接口封装了各种可能的资源类型,对使用者屏蔽了文件类型的不同。
对资源的提供者有两个问题,
(1)如何把资源包装起来交给其他人用。 Resource 接口继承了 InputStreamSource 接口,接口中有个 getInputStream 方法,返回的是 InputStream 类。这样所有资源都通过 InputStream 类来获取,达到屏蔽资源的提供者。
(2)加载资源,也就是资源的加载者要统一。从上图中可看出这个任务是由 ResourceLoader 接口完成,它屏蔽了所有资源加载者的差异,只需实现这个接口就可加载所有资源,默认实现是 DefaultResourceLoader。
Context 和 Resource 是如何建立关系的?首先看一下类关系图:

浅析Spring框架设计
图 8. Context 和 Resource 的类关系图
从上图可以看出,Context 是把资源的加载、解析和描述工作委托给了 ResourcePatternResolver 类来完成,相当于一个接头人,他把资源的加载、解析和资源的定义整合在一起便于其他组件使用。Core 组件中还有很多类似的方式。
五、Ioc 容器如何工作
1、如何创建 BeanFactory 工厂
正如图 2 描述,Ioc 容器实际上就是 Context 组件结合其他两个组件共同构建了一个 Bean 关系网,构建的入口就在AbstractApplicationContext 类的 refresh 方法中。代码如下:
AbstractApplicationContext.refresh    
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();
        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);
        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);
            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            // Initialize message source for this context.
            initMessageSource();
            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();
            // Initialize other special beans in specific context subclasses.
            onRefresh();
            // Check for listener beans and register them.
            registerListeners();
            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);
            // Last step: publish corresponding event.
            finishRefresh();
        }
        catch (BeansException ex) {
            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();
            // Reset 'active' flag.
            cancelRefresh(ex);
            // Propagate exception to caller.
            throw ex;
        }
    }
}
这个方法就构建整个 Ioc 容器过程的完整的代码,了解了里面的每一行代码基本上就了解大部分 Spring 的原理和功能了。
这段代码主要包含几个步骤:
(1)构建 BeanFactory,以便于产生所需的“演员”
(2)注册可能感兴趣的事件
(3)创建 Bean 实例对象
(4)触发被监听的事件
下面结合代码分析这几个过程。
第二三句就是创建和配置 BeanFactory。这里 refresh 也就是刷新配置,前面 Context 有可更新的子类,这里正是实现这个功能,当BeanFactory 已存在则更新,没有就新创建。下面是更新 BeanFactory 的方法代码:
AbstractRefreshableApplicationContext. refreshBeanFactory    
protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}
这个方法实现了 AbstractApplicationContext 的抽象方法 refreshBeanFactory,这段代码说明了 BeanFactory 的创建过程。注意 BeanFactory 对象的类型的变化,它有很多子类,什么情况下使用不同的子类非常关键。BeanFactory 的原始对象是 DefaultListableBeanFactory,下面看一下这个类的继承层次类图:

浅析Spring框架设计
图 9. DefaultListableBeanFactory 类继承关系图
从这个图中发现除了 BeanFactory 相关的类外,还发现了与 Bean 的 register 相关。这在 refreshBeanFactory 方法中有一行 loadBeanDefinitions(beanFactory) 将找到答案,这个方法将开始加载、解析 Bean 的定义,也就是把用户定义的数据结构转化为 Ioc 容器中的特定数据结构。
这个过程用下面时序图解释:

浅析Spring框架设计
图 10. 创建 BeanFactory 时序图
Bean 的解析和登记流程时序图如下:

浅析Spring框架设计
图 11. 解析和登记 Bean 对象时序图
创建好 BeanFactory 后,接下去添加一些 Spring 本身需要的工具类,这个操作在 AbstractApplicationContext 的 prepareBeanFactory 方法完成。
AbstractApplicationContext 中接下来的三行代码对 Spring 的功能扩展性起了至关重要的作用。前两行主要是让你现在可以对已经构建的 BeanFactory 配置做修改,后面一行就是让你可以对以后再创建 Bean 的实例对象时添加一些自定义的操作。所以他们都是扩展了 Spring 的功能,所以学习使用 Spring 必须对这一部分搞清楚。
其中在 invokeBeanFactoryPostProcessors 方法中主要是获取实现 BeanFactoryPostProcessor 接口的子类。并执行它的 postProcessBeanFactory 方法,这个方法的声明如下:
BeanFactoryPostProcessor.postProcessBeanFactory    
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)  throws BeansException;
它的参数是 beanFactory,说明可以对 beanFactory 做修改,这里注意这个 beanFactory 是 ConfigurableListableBeanFactory 类型的,这印证了不同 BeanFactory 所使用的场合不同,这里只能是可配置的 BeanFactory,防止一些数据被用户随意修改。
registerBeanPostProcessors 方法也是可以获取用户定义的实现了 BeanPostProcessor 接口的子类,并执行把它们注册到 BeanFactory 对象中的 beanPostProcessors 变量中。BeanPostProcessor 中声明了两个方法:postProcessBeforeInitialization、postProcessAfterInitialization 分别用于在 Bean 对象初始化时执行。可以执行用户自定义的操作。
后面的几行代码是初始化监听事件和对系统的其他监听者的注册,监听者必须是 ApplicationListener 的子类。
2、如何创建 Bean 实例并构建 Bean 的关系网
下面就是 Bean 的实例化代码,是从 finishBeanFactoryInitialization 方法开始的。
AbstractApplicationContext.finishBeanFactoryInitialization 
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // Stop using the temporary ClassLoader for type matching.
    beanFactory.setTempClassLoader(null);
    // Allow for caching all bean definition metadata, not expecting further changes.
    beanFactory.freezeConfiguration();
    // Instantiate all remaining (non-lazy-init) singletons.
    beanFactory.preInstantiateSingletons();
}
从上面代码中可以发现 Bean 的实例化是在 BeanFactory 中发生的。preInstantiateSingletons 方法的代码如下:
DefaultListableBeanFactory.preInstantiateSingletons   
public void preInstantiateSingletons() throws BeansException {
    if (this.logger.isInfoEnabled()) {
        this.logger.info("Pre-instantiating singletons in " + this);
    }
    synchronized (this.beanDefinitionMap) {
        for (String beanName : this.beanDefinitionNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton()
                && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    final FactoryBean factory =
                        (FactoryBean) getBean(FACTORY_BEAN_PREFIX+ beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null
                        && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(
                            new PrivilegedAction<Boolean>() {
                            public Boolean run() {
                                return ((SmartFactoryBean) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        isEagerInit = factory instanceof SmartFactoryBean
                            && ((SmartFactoryBean) factory).isEagerInit();
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
                else {
                    getBean(beanName);
                }
            }
        }
    }
}
 这里出现了非常重要的 Bean —— FactoryBean,Spring 一大半扩展的功能都与这个 Bean 有关,这是个特殊的 Bean 是个工厂 Bean,可以产生 Bean 的 Bean,这里产生 Bean 是指 Bean 的实例,如果一个类继承 FactoryBean 用户可自定义产生实例对象的方法只要实现getObject 方法。然而在 Spring 内部这个 Bean 的实例对象是 FactoryBean,通过调用这个对象的 getObject 方法就能获取用户自定义产生的对象,从而为 Spring 提供了很好的扩展性。Spring 获取 FactoryBean 本身的对象是在前面加上 & 来完成。
如何创建 Bean 的实例对象以及如何构建 Bean 实例对象之间的关联关系式 Spring 中的一个核心关键,下面是这个过程的流程图。

浅析Spring框架设计
图 12.Bean 实例创建流程图
如果是普通的 Bean 就直接创建他的实例,是通过调用 getBean 方法。下面是创建 Bean 实例的时序图:

浅析Spring框架设计
图 13.Bean 实例创建时序图
还有一个非常重要部分就是建立 Bean 对象实例之间的关系,这也是 Spring 框架的核心竞争力,何时、如何建立他们之间的关系请看下面的时序图:

浅析Spring框架设计
图 14.Bean 对象关系建立
六、Ioc 容器的扩展点
Spring 的 Ioc 容器主要有BeanFactoryPostProcessor, BeanPostProcessor。分别是在构建 BeanFactory 和构建 Bean 对象时调用。还有InitializingBean 和 DisposableBean 分别是在 Bean 实例创建和销毁时被调用。用户可实现这些接口中定义的方法,Spring 就会在适当时候调用他们。还有一个是 FactoryBean 他是个特殊的 Bean,这个 Bean 可以被用户更多的控制。
这些扩展点通常是使用 Spring 来完成特定任务的地方,如何精通 Spring 就看有没有掌握好 Spring 的扩展点并和它们的内在机理,进而懂得如何使用它们。
把 Ioc 容器比作一个箱子,这个箱子里有若干个球的模子,可用这些模子造很多种不同的球,还有一个造这些球模的机器,这个机器可产生球模子。那么 BeanFactory 就是那个造球模的机器,球模就是 Bean,而球模造出来的球就是 Bean 的实例。 BeanFactoryPostProcessor 对应到当造球模被造出来时,将有机会对其做出适当的修正,也就是它可帮你修改球模。而 InitializingBean 和 DisposableBean 是在球模造球的开始和结束阶段,可完成一些预备和扫尾工作。BeanPostProcessor 可让你对球模造出来的球做出适当的修正。 FactoryBean是个神奇的球模。这个球模预先不定型,而是由你来确定它的形状,既然可以确定球模型的形状,当然造出来的球肯定就是你想要的球了,这样在这个箱子里可以发现所有你想要的球。
七、Ioc 容器如何为我所用
使用 Spring 必须要首先构建 Ioc 容器,ApplicatonContext.xml 就是 Ioc 容器的默认配置文件,Spring 的所有特性功能都是基于这个 Ioc 容器工作,比如 AOP。
Ioc 实际上是构建了一个魔方,Spring 搭好了骨骼架构,这个魔方到底能变出什么东西,在于你的参与。如何参与?就要了解 Spring 中那有些扩展点,我们通过实现那些扩展点来改变 Spring 的通用行为。至于如何实现扩展点来得到想要的个性结果,Spring 中有很多例子,其中 AOP 的实现就是 Spring 本身实现了其扩展点来达到了想要的特性功能。