文章目录
IoC容器的实现
一、Spring IoC容器概述
1、IoC容器和依赖反转模式
维基百科中对“依赖反转”的定义中有一句话:
- Martin Fowler为控制反转创造了一个更好的名字:依赖注入
许多非凡的应用都是由两个或者多个类通过彼此的合作来实现业务逻辑的,这使得每个对象都需要与其合作的对象的引用,如果这个获取过程要靠自身实现,那么如你所见,这将导致代码高度耦合并且难以测试。
如果合作对象的引用或者依赖关系的管理由具体对象来完成,会导致代码的高度耦合和可测试性的降低,这对复杂的面向对象系统的设计是非常不利的。
依赖控制反转的实现有很多种方式。在Spring中,IoC就是这个模式的载体,它可以在对象生成或初始化时直接将数据注入到对象中,也可以通过将对象引用注入到对象数据域中的方式来注入对方法调用的依赖。
注意
应用控制反转后,当对象被创建时,由一个调控系统内的所有对象的外界实体将其依赖的对象的引用传递给它,即依赖被注入到对象中,所以,控制反转是关于一个对象如何获取它所依赖的对象的引用。我们可以认为上面提到的调控系统是应用平台,或者更具体的说是IoC容器。通过使用IoC容器,对象依赖关系的管理被反转了,转到了IoC容器中来,对象之间的相互依赖关系由IoC容器进行管理,并由IoC完成对象的注入。
2、Spring IoC的应用场景
容器的理解:
容器可以管理对象的生命周期,对象与对象之间的依赖关系,您可以使用一个配置文件(通常是XML),在上面定义好对象的名称、如何产生(Prototype方式或者Singleton方式),哪个对象产生之后必须设定成为某个对象的属性等,在启动容器后,所有的对象可直接取用,不用编写任一行代码来产生对象或者建立对象之间的依赖关系。
Spring IoC提供了一个基本的JavaBean容器,通过IoC模式管理依赖关系,并通过依赖注入和AOP切面增强了为JavaBean这样的POJO对象赋予事务管理、生命周期管理等基本功能。
在具体的注入实现中,接口注入、setter注入、构造器注入是主要的注入方式。在Spring IoC的设计中,setter注入和构造器注入是主要的注入方式,相对而言,使用时setter注入是常见的注入方式。
二、IoC容器系列的实际与实现:BeanFactory(基本)和ApplicationContext(高级)
在Spring IoC容器的设计中,我们可以看到两个主要的容器系列,一个是实现BeanFactory接口的简单容器系列,这系列容器只实现容器的最基本的功能,另外一个是ApplicationContext应用上下文,它作为容器的高级形态而存在,应用上下文在简单容器的基础上增加了许多面向框架的特性,同时对应用环境作了许多适配。
1、Spring IoC容器系列
IoC容器为开发者管理对象之间的依赖关系提供了许多便利和基础服务,有许多IoC容器供开发者选择,SpringFramework的IoC容器就是其中一个。
到底什么是IoC容器呢?
- 我们经常接触到的BeanFactory和ApplicationContext都可以看成是容器的具体表现形式,可以把容器理解成水桶,只要能装水,尽管水桶是各种样子的,只要具备水桶的基本特性就可以,在Spring中也是一样,Spring有各种各样的IoC容器供用户选择和使用,使用什么样的容器完全取决于用户的需要,但是在使用之前如果能够了解容器的基本情况,那对容器的使用是非常有帮助的。
就像商品需要有产品规格说明一样,同样,作为IoC容器,也需要为它的具体的实现指定基本的功能规范,这个功能规范的设计表现为接口BeanFactory,它体现了Spring为提供给用户使用的IoC容器所设定的最基本的功能规范。
Spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及它们之间的相互依赖关系。BeanDifition抽象了我们对Bean的定义,是让容器起作用的主要的数据类型。I我们都知道,在计算机的世界里,所有的功能都是建立在通过数据对显示进行抽象的基础上的。oC容器是用来管理对象依赖关系的,对IoC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成的。这些BeanDefinition就像是容器里装的水,有了这些基本数据,容器才能发挥作用。
2、Spring IoC容器的设计
从下图可以看出BeanFactory接口在继承体系中的地位,它是作为一个最基本的接口类出现在Spring的IoC容器体系中的。
- 1、从接口BeanFactory到HierarchicalBeanFactory (Hierarchical 英[ˌhaɪəˈrɑːkɪkl]
按等级划分的; 等级制度的;),再到ConfigurableBeanFactory,是一个主要的BeanFactory设计路径。
在这条设计路径中,BeanFactory接口定义了基本的IoC容器的规范,在这个接口定义中,包括了getBean()这样的容器的基本方法。
HierarchicalBeanFactory接口在继承了BeanFactory接口之后,增加了getParentBeanFactory()的接口功能,使得BeanFactory具备了双亲IoC容器的管理功能。
在接下来的ConfigurableBeanFactory接口中,主要定义了一些对BeanFactory的配置功能,比如setParentFactory()设计双亲IoC容器,通过addBeanPostProcessor()配置Bean的后置处理器。
下图显示了上面说的三个接口的方法。
- 2、第二条设计主线是以ApplicationContext上下文接口为核心的接口设计,这里设计的主要的接口设计有BeanFactory到ListableBeanFactory,再到ApplicationContext,再到我们常用的WebApplicationContext或者ConfigurableApplicationContext,我们常用的应用上下文基本都是WebApplicationContext或者ConfigurableApplicationContext实现的。在这个接口体系中,HierarchicalBeanFactory和ListableBeanFactory两个接口连接BeanFactory接口定义和ApplicationContext应用上下文接口定义。
在ListableBeanFactory接口中,细化了许多BeanFactory的接口功能,比如定义了getBeanDefinitionNames()接口方法,ApplicationContext接口通过继承MessageSource、ResourceLoader、ApplicationEventPublisher接口,在BeanFactory简单IoC的基础上添加了许多对高级容器的特性的支持。
- 3、具体的IoC容器都是在这个接口体系下实现的,比如DefaultListableBeanFactory,这个基本IoC容器的实现就是实现了ConfigurableBeanFactory,从而成为了一个简单的IoC容器的实现,像其他IoC容器,比如XmlBeanFactory都是在DefaultListableBeanFactory的基础上做扩展,同样的,ApplicationContext的实现也是如此。
- 4、在ApplicationContext的设计中,一方面可以看到它继承了BeanFactory体系中的ListableBeanFactory、AutowireCapableBeanFactory、HierarchicalBeanFactory等BeanFactory的接口,具备了IoC容器的基本功能,另一方面通过继承MessageSource、ResourceLoader、ApplicationEventPublisher接口,赋予了更高级的IoC容器特性。对于这些ApplicationContext而言,为了在Web环境中使用它,还设计了WebApplicationContext接口,而这个接口通过继承ThemeSource接口来扩充功能。
BeanFactory的应用场景
在Spring的代码实现中,BeanFactory只是一个接口类,并没有给出容器的具体实现,而DefaultListableBeanFactory、XmlBeanFactory、XmlClassPathApplicationContext等都可以看成是容器附加了某种功能的具体实现,也就是容器体系中的具体容器产品。
用户使用容器的时候可以使用&来获得FactoryBean本身,用来区分通过容器来获取FactoryBean产生的对象和获取FactoryBean本身。举例子说明,如果myJndiObject是一个FactoryBean,那么使用&myJndiObject得到的是FactoryBean,而不是myJndiObject这个FactoryBean产生出来的对象。
FactoryBean和BeanFactory的区别:
- 一个是Factory,一个是Bean
- 在Spring中,所有的Bean都是由BeanFactory来进行管理的。
- 对于FactoryBean而言,这个Bean不是简单的Bean,而是一个能产生或者修饰对象生成的工厂Bean。
有了BeanFactory的定义,用户可以实现以下操作:
- 1、通过接口方法containsBean让用户能够判断容器是否包含指定名字的Bean
- 2、通过接口方法isSingleton来查询指定名字的Bean是否是Singleton类型的Bean。对于Singleton属性,用户可以在BeanDefinition中指定。
- 3、通过接口方法isPrototype来查询指定名字的Bean是否是prototype类型的Bean。与Singleton属性一样,用户可以在BeanDefinition中指定。
- 4、通过接口方法isTypeMatch来查询指定名字的Bean的Class类型是否是特定的Class类型,这个Class类型可以由用户来指定。
- 5、通过方法getType来查询指定名字的Bean的Class类型
- 6、通过方法getAliases来查询指定了名字的Bean的所有别名,这些别名都是用户在BeanDefinition中定义的。
BeanFactory容器的设计原理
Spring提供了符合IoC容器接口的一系列容器的实现供开发人员使用,我们以XmlBeanFactory的实现为例来说明简单IoC容器的设计原理。
XmlBeanFactory实现了BeanFactory,没有实现ApplicationContext,它提供了IoC容器的基本的功能。
下面是XmlBeanFactory的代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.beans.factory.xml;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.io.Resource;
/** @deprecated */
@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 ,后者非常重要,是我们经常使用的一个IoC的实现,它包含了基本IoC容器所具有的重要功能,也是很多地方都会用到的容器系列的一个基本产品。
在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的。XmlBeanFactory在继承DefaultListableBeanFactory的同时,增加了新的功能,这些功能很容易从名字看出,它是一个与xml相关的BeanFactory,也就是说它是可以读取以XML文件方式定义的BeanDefinition的IoC容器。
这些实现XML读取的功能是怎么实现的?
- 对这些XML文件定义信息的处理并不少由XmlBeanFactory直接完成的,在XmlBeanFactory中,初始化了一个XmlBeanDefinitionReader对象,有了这个Reader对象,那些XML方式定义的BeanDefinition就有了处理的地方。
在构造XmlBeanFactory这个IoC容器的时候,需要指定BeanDefinition信息的来源,而这个信息来源封装成Spring中的Resource类来给出。Resource是Spring用来封装IO操作的类。
比如:
- ClassPathResource res = new ClassPathResource(“beans.xml”);这样具体的ClassPathResource来构造需要的Resource,然后将Resource作为构造参数传递给XmlBeanFactory构造函数,这样IoC容器就可以方便的定位到需要的BeanDefinition信息来对Bean完成容器的初始化和依赖注入过程。
reader 从XmlBeanFactory源码可以看出,使用了loadDefinitions()方法从Resource中载入BeanDefinitions,LoadBeanDefinition同时也是IoC容器初始化的重要组成部分。
参考下面的XmlBeanFactory的实现,我们以编程的方式使用DefaultListableBeanFactory,从中可以看到IoC容器使用的一些基本过程,对我们了解IoC的工作原理是非常有帮助的。
XmlBeanDefinitionReader实现了BeanDefinitionReader接口,覆写了loadBeanDefinitions方法。
使用IoC容器的时候,需要如下几个步骤:
- 1、创建IoC配置文件的抽象资源,这个资源包含了BeanDefinition的定义信息
- 2、创建一个BeanFactory,这里使用DefaultListableBeanFactory
- 3、创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来加入XML文件形式的BeanDefinition。通过一个回调配置给BeanFactory。
- 4、从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入和注入Bean定义之后,需要的IoC容器就建立起来了,这个时候就可以直接使用IoC容器了。
ApplicationContext的应用场景
上一节中了解了IoC容器的建立的基本步骤。理解这些步骤可以方便通过编程的方式控制这些配置和容器的建立过程。但是在Spring中,系统已经为用户提供了许多已经定义好的容器的实现,相比那些简单扩展BeanFactory的基本的IoC容器,可以使用高级形态意义的IoC容器ApplicationContext。
- 1、支持不同的信息源。ApplicationContext扩展了MessageSource接口,这些信息源的扩展功能可以支持国际化的实现,为开发语言版本的应用提供服务。
- 2、访问资源。我们可以从不同地方的到Bean定义资源。这种抽象使得用户程序可以灵活定义Bean定义信息。尤其是从不同的IO途径得到Bean定义信息。
- 3、支持应用事件。继承了ApplicationEventPublisher,从而在上下文中引入事件机制。这些事件和Bean的生命周期的结合为Bean的管理提供了便利。
- 4、一般建立在开发应用的时候使用Application作为IoC容器的基本形式。
ApplicationContext容器的设计原理
下面以常用的FileSystemXmlApplicationContext的实现为例来说明ApplicationContext容器的设计原理。
在FileSystemXmlApplicationContext的设计中,我们看到ApplicationContext应用上下文的主要功能已经在FileSystemXmlApplicationContext中实现了。FileSystemXmlApplicationContext作为一个具体的应用上下文,只需要实现和它自身设计相关的两个功能。
- 一个是构造函数中调用的refresh。
这个refresh函数会涉及IoC容器启动的一系列复杂操作。 - 另外一个功能是FileSystemXmlApplicationContext设计具体相关的功能,这部分与怎么样从文件系统中加载XML的Bean的定义资源有关。