Spring技术内幕 :IoC容器的实现(二)—— IoC容器系列的设计与实现:BeanFactory和ApplicationContext

  在上一篇Spring技术内幕 :IoC容器的实现(一)—— Spring IoC的概述中,我们简单回顾了一下Spring IoC容器中依赖反转的概念以及Spring IoC的应用场景,本文将开始简单了解一下具体容器的继承关系及应用场景。

  在Spring IoC容器的设计中,可以看到主要的两个容器系列,一个是实现BeanFactory接口的简单接口系列,这个系列的容器只实现了最基本的功能;另一个是ApplicationContext应用上下文,作为容器的高级形态存在。应用上下文在简单容器的基础上,增加了许多面向框架的特性,同时对应用环境作了许多适配。有了这两种基本的容器系列,基本上就可以满足用户对IoC容器使用的大部分需求了。

1 Spring的IoC容器系列

  IoC容器为开发者管理对象之间的依赖关系提供了很多便利和基础服务。对于IoC容器的使用者来说,、经常接触到BeanFactory和ApplicationContext都可以看成是容器的具体表现形式。同时,在Spring中有各式各样的IoC容器的实现供用户选择和使用,使用什么样的容器完全取决于用户的需求。下图展示了这个容器系列的概况。

  从上图可以看出,BeanFactory作为一个最基本的接口类出现在容器体系中。由此可以看出,作为IoC容器,接口类BeanFactory为提供给用户使用的IoC容器所设定了基本的功能规范。对于具体的IoC容器,需要满足BeanFactory这个基本的接口定义,所以在可以看到BeanFactory接口在继承体系中的作为最基本的接口类出现在Spring的IoC容器体系中。

  在Spring提供的基本的IoC容器的接口定义和实现的基础上,Spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及他们之间的相互依赖关系。我们知道,IoC容器是用来管理对象依赖关系的,对IoC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转的核心数据结构,依赖反转功能都是围绕BeanDefinition的处理完成的。

2 Spring IoC容器的设计

  本节我们将简单了解IoC容器是如何设计的,首先通过下图来了解一下IoC容器的接口设计图。


  下面对接口关系做一些简要的分析

  • 从BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,是一条主要的BeanFactory设计路径。其中,BeanFactory接口中定义了基本的IoC容器的规范。在这个接口定义中包括了getBean()这样的基本方法。HierarchicalBeanFactory接口在继承了BeanFactory接口后,增加了getParentBeanFactory()的接口功能,使BeanFactory具备了双亲IoC的管理能力。在接下来的ConfigurableBeanFactory接口中,主要定义了一些对BeanFactory的配置功能,比如setParentBeanFactory()设置双亲IoC容器,通过addBeanPostProcessor()配置Bean后置处理器等。通过这些接口设计的叠加,定义了BeanFactory就是简单IoC容器的基本功能
  • 第二条接口对的设计主线,以ApplicationContext应用上下文接口为核心的接口设计,主要涉及的接口设计有,从BeanFactory到ListableBeanFactory,再到ApplicationContext,再到常用的WebApplicationContext或ConfigurableApplicationContext接口。在这个接口体系中,ListableBeanFactory和HierarchicalBeanFactory两个接口,连接BeanFactory接口定义和ApplicationContext应用上下文的接口定义。在ListableBeanFactory中细化了许多BeanFactory的接口功能,如定义了getBeanDefinitionNames()接口方法;HierarchicalBeanFactory接口在上面已经简单介绍;对于ApplicationContext,它继承了MessageSource、ResourceLoader、ApplicationEvnetPublisher接口,在BeanFactory的简单IoC容器的基础上添加了许多对高级容器的特性的支持。

  在图中主要涉及了接口关系,任何具体的IoC容器都是在这个接口体系下实现,再进行功能上的扩展。总体而言,ApplicationContext既继承了BeanFactory IoC的基本功能,有继承了MessageSource、ResourceLoader、ApplicationEvnetPublisher接口,实现了对简单的IoC容器功能扩展的目的,即BeanFactory为IoC容器的基本形式,而各种ApplicationContext的实现是IoC容器对的高级表现形式。

BeanFactory的应用场景

  BeanFactory提供了最基本的IoC容器的功能。

  用户可以使用转义符“&”来得到FactoryBean本身,用来区分通过容器来获取FactoryBean产生的对象和FactoryBean本身,即使用“&”得到的是FactoryBean本身。

  注意:需要区分一下BeanFactory和FactoryBean的区别。BeanFactory从字面角度就可以知道它是一个Factory,是IoC的对象工厂,管理所有的Bean。FactoryBean则是一个Bean,是一个能产生或修饰对象生成的工厂类,它的实现与设计模式中的工厂模式和修饰器模式类似。

  BeanFactory中提供了IoC的基本功能,有了它们,用户可以执行以下操作:

  1. containsBean:判断容器中是否含有指定名字的Bean。
  2. isSingleton:查询指定名字的Bean是否是Singleton类型的Bean。Singleton属性可以在BeanDefinition中指定。
  3. isPrototype:查询指定名字的Bean是否是Prototype类型的Bean。Prototype属性也可以在BeanDefinition中指定。
  4. isTypeMatch:查询指定了名字的Bean的Class类型是否是特定的Class类型。Class类型可以由用户指定。
  5. getType:查询指定名字的Bean的Class类型
  6. getAliases:查询指定了名字的Bean的所有别名,别名可以再BeanDefinition中定义。

  以下为BeanFactory的源码清单。

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";
    Object getBean(String var1) throws BeansException;
    <T> T getBean(String var1, Class<T> var2) throws BeansException;
    Object getBean(String var1, Object... var2) throws BeansException;
    <T> T getBean(Class<T> var1) throws BeansException;
    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;
    boolean containsBean(String var1);
    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
    String[] getAliases(String var1);
}

BeanFactory容器的设计原理

  虽然XmlBeanFactory在新版本Spring中已经不建议使用,但他相对简单,我们还是以XmlBeanFactory的实现来说明简单IoC容器的设计原理。下图为XmlBeanFactory的设计类继承关系。

  以下为XmlBeanFactory源码清单。

@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {
    private final XmlBeanDefinitionReader reader;

    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, (BeanFactory)null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader = new XmlBeanDefinitionReader(this);
        this.reader.loadBeanDefinitions(resource);
    }
}

  可以看到,XmlBeanFactory继承了DefaultListableBeanFactory,需要明确,DefaultListableBeanFactory这个类非常重要,它实际上包含了基本IoC所具有的重要功能。并且在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的。

  从源码中可以看到,XmlBeanFactory在继承了DefaultListableBeanFactory后有添加了关于XML的新的功能。它是一个可以读取XML文件方式定义的BeanDefinition的IoC容器。

  在构造XmlBeanFactory时,需要指定BeanDefinition的信息来源,封装为Resource(Spring中由来封装I/O操作的类)对象。将Resource作为构造参数传递给XmlBeanFactory,IoC容器就可以方便地定位到需要的BeanDefinition信息来对Bean完成容器的初始化和依赖注入过程。对XmlBeanDefinitionReader对象的初始化,以及使用这个对象对loadBeanDefinitions的调用载入BeanDefinitions的过程。

ApplicationContext的应用场景

  首先通过下图了解一下ApplicationContext提供的BeanFactory不具备的新特性


  • 支持不同的信息源。扩展MessageSource接口,信息源的扩展功能可以支持国际化的实现。
  • 访问资源。体现在对ResouceLoader和Resource的支持上,可以从不同的I/O途径获取Bean的定义信息。
  • 支持应用事件。它继承了ApplicationEventPublisher,在上下文中引入事件机制。
  • 在ApplicationContext中提供的附加服务,使得ApplicationContext与简单的BeanFactory相比,对它的使用是一种面向框架的使用。

ApplicationContext容器的设计原理

  我们以常用的FileSystemXmlApplicationContext的实现为例来说明ApplicationContext容器的设计原理。FileSystemXmlApplicationContext源码清单如下所示。

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
    public FileSystemXmlApplicationContext() {
    }

    public FileSystemXmlApplicationContext(ApplicationContext parent) {
        super(parent);
    }

    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[]{configLocation}, true, (ApplicationContext)null);
    }

    public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, (ApplicationContext)null);
    }

    public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
        this(configLocations, true, parent);
    }

    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
        this(configLocations, refresh, (ApplicationContext)null);
    }

    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
        super(parent);
        this.setConfigLocations(configLocations);
        if (refresh) {
            this.refresh();
        }

    }

    protected Resource getResourceByPath(String path) {
        if (path != null && path.startsWith("/")) {
            path = path.substring(1);
        }

        return new FileSystemResource(path);
    }
}

  可以看到,FileSystemXmlApplicationContext继承了AbstractXmlApplicationContext ,其中AbstractXmlApplicationContext 实现了ApplicationContext应用上下文的主要功能。在FileSystemXmlApplicationContext中它主要实现了与自身设计的两个功能。

  第一个为,如果应用直接使用FileSystemXmlApplicationContext,对于实例化这个应用的上下文的支持,同时启动refresh()过程,对应了FileSystemXmlApplicationContext中的三参构造方法。另外一个功能是与FileSystemXmlApplicationContext设计具体相关的功能,与怎样从文件系统中加载XML的Bean定义资源有关。对应了getResourceByPath(String path)方法。


  本文从BeanFactory和ApplicationContext两个系列简单介绍了IoC容器的设计原理,并通过部分源码体会了具体的IoC容器是如何在以前的基础上进一步完善功能的。


猜你喜欢

转载自blog.csdn.net/m0_37135421/article/details/80592098