【1】IOC容器与依赖反转模式
依赖反转是指依赖对象的获得被反转了。依赖控制反转的实现有很多方式,Spring中IOC容器是实现这个模式的载体,它可以在对象生成或初始化时直接将数据注入到对象中,也可以通过将对象引用注入到对象数据域中的方式来注入对方法调用的依赖。这种依赖注入是可以递归的,对象被逐层注入。
通过使用IOC容器,对象依赖关系的管理被反转了,转到IOC容器中来了,对象之间的相互依赖关系由IOC容器进行管理,并由IOC容器完成对象的注入。
这里的“反转”可以认为是“责任”的反转,把责任交给了容器
。也就是说依赖对象的获得被反转了,把控制权从具体业务对象手中转交到平台或者框架手中。
在Spring中,Spring IOC提供了一个基本的JavaBean容器,通过IOC模式管理依赖关系,并通过依赖注入和AOP切面增强了为JavaBean这样的POJO对象赋予事务管理、生命周期管理等基本功能。
通过使用IOC容器,对象依赖关系的管理被反转了,转到IOC容器中。对象之间的相互依赖关系由IOC容器进行管理,并由IOC容器完成对象的注入。
在Spring IOC容器的设计中,我们可以看到两个主要的容器系列:
一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器的最基本功能;
另一个是ApplicationContext应用上下文,它作为容器的高级形态而存在。应用上下文在简单容器的基础上,增加了许多面向框架的特性,同时对应用环境作了许多适配。
BeanFactory体现了Spring为提供给用户使用的IOC容器所设定的最基本的功能规范。在这些Spring提供的基本IOC容器的接口定义和实现的基础上,Spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及它们之间的相互依赖关系。
BeanDefinition抽象了我们对Bean的定义,是让容器起作用的主要数据类型。我们都知道,在计算机世界里,所有的功能都是建立在通过数据对现实进行抽象的基础上的。IOC容器是用来管理对象依赖关系的,对IOC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对整个BeanDefinition的处理来完成的。这些BeanDefinition就像是容器里装的水,有了这些基本数据,容器才能够发挥作用。
BeanFactory 一些常见接口释义
① BeanDefinitionRegistry注册表
Spring 配置文件中每一个节点元素在 Spring 容器里都通过一个 BeanDefinition 对象表示,它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器手工注册 BeanDefinition 对象的方法。
② BeanFactory 顶层接口
位于类结构树的顶端 ,它最主要的方法就是 getBean(String beanName),该方法从容器中返回特定名称的 Bean,BeanFactory 的功能通过其他的接口得到不断扩展。
③ ListableBeanFactory
该接口定义了访问容器中 Bean 基本信息的若干方法,如查看 BeanDefinition的个数、获取某一类型 Bean的配置名、查看容器中是否包括某一 BeanDefinition等方法。
④ HierarchicalBeanFactory父子级联
父子级联 IoC 容器的接口,子容器可以通过接口方法访问父容器。 通过HierarchicalBeanFactory 接口, Spring 的 IoC 容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的 Bean,但父容器不能访问子容器的 Bean。
public interface HierarchicalBeanFactory extends BeanFactory { @Nullable BeanFactory getParentBeanFactory(); boolean containsLocalBean(String name); }
Spring 使用父子容器实现了很多功能,比如在Spring MVC 中,展现层 Bean 位于一个子容器中,而业务层和持久层的 Bean 位于父容器中。这样,展现层 Bean 就可以引用业务层和持久层的 Bean,而业务层和持久层的 Bean 则看不到展现层的 Bean。
⑤ ConfigurableBeanFactory
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry
是一个重要的接口,增强了 IoC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法。
⑥ AutowireCapableBeanFactory 自动装配
定义了将容器中的 Bean 按某种规则(如按名字匹配、按类型匹配等)进行自动装配的方法。
⑦ SingletonBeanRegistry运行期间注册单例Bean
定义了允许在运行期间向容器注册单实例 Bean 的方法;对于单实例( singleton)的 Bean 来说,BeanFactory 会缓存 Bean 实例,所以第二次使用 getBean() 获取 Bean 时将直接从 IoC 容器的缓存中获取 Bean 实例。
Spring 在 DefaultSingletonBeanRegistry 类中提供了一个用于缓存单实例 Bean 的缓存器,它是一个用 HashMap 实现的缓存器,单实例的 Bean 以 beanName 为键保存在这个 HashMap 中。
public interface SingletonBeanRegistry { void registerSingleton(String beanName, Object singletonObject); @Nullable Object getSingleton(String beanName); boolean containsSingleton(String beanName); String[] getSingletonNames(); int getSingletonCount(); Object getSingletonMutex(); }
⑧ ConfigurableListableBeanFactory
除了ConfigurableBeanFactory提供的功能之外,它还提供了分析和修改bean定义以及预实例化单例的方法。这个接口只是为了允许框架内部即插即用,即使在需要访问bean工厂配置方法时也是如此。
public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory
ApplicationContext 一些常见接口释义
① ClassPathXmlApplicationContext
默认从类路径加载配置文件
② FileSystemXmlApplicationContext
默认从文件系统中装载配置文件
③ ApplicationEventPublisher
让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件等。
④ MessageSource
为应用提供 i18n 国际化消息访问的功能;
⑤ ResourcePatternResolver
所 有 ApplicationContext 实现类都实现了类似于PathMatchingResourcePatternResolver 的功能,可以通过带前缀的 Ant 风格的资源文件路径装载 Spring 的配置文件。
⑥ LifeCycle
该接口是 Spring 2.0 加入的,该接口提供了 start()和 stop()两个方法,主要用于控制异步处理过程。在具体使用时,该接口同时被 ApplicationContext 实现及具体 Bean 实现,ApplicationContext 会将 start/stop 的信息传递给容器中所有实现了该接口的 Bean,以达到管理和控制 JMX、任务调度等目的。
⑦ ConfigurableApplicationContext
扩展于 ApplicationContext,它新增加了两个主要的方法:refresh()和 close(),让 ApplicationContext 具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用 refresh()即可启动应用上下文,在已经启动的状态下,调用 refresh()则清除缓存并重新装载配置信息,而调用 close()则可关闭应用上下文。
⑧ WebApplicationContext
是专门为 Web 应用准备的,它允许从相对于 Web 根目录的路径中装载配置文件完成初始化工作。从 WebApplicationContext 中可以获得 ServletContext 的引用,整个 Web 应用上下文对象将作为属性放置到 ServletContext 中,以便 Web 应用环境可以访问 Spring 应用上下文。
【2】Spring IOC容器的设计
下图描述了Spring IOC容器中的主要接口设计。
① 从接口BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,是一条主要的BeanFactory设计路径。
在这条接口设计路径中,BeanFactory接口定义了基本的IOC容器的规范。在这个接口定义中,包括了getBean()这样的IOC容器的基本方法(通过这个方法可以从容器中取得Bean)。
而HierarchicalBeanFactory接口在继承了BeanFactory的基本接口之后,增加了getParentBeanFactory()的接口功能,使BeanFactory具备了双亲IOC容器的管理功能。
在接下里的ConfigurableBeanFactory接口中,主要定义了一些对BeanFactory的配置功能,比如通过setParentBeanFactory设置双亲IOC容器,通过addBeanPostProcessor配置Bean后置处理器等等。通过这些接口设计的叠加,定义了BeanFactory就是简单IOC容器的基本功能。
BeanFactory有三个二级子接口:HierarchicalBeanFactory、AutowireCapableBeanFactory和ListableBeanFactory。
ConfigurableBeanFactory可以被称为三级接口,对二级接口HierarchicalBeanFactory进行了再次增强,它还继承了另一个外来的接口SingletonBeanRegistry。
ConfigurableListableBeanFactory可以被称为四级接口/三级接口,其继承了ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory。这4级接口是BeanFactory的基本接口体系。
ListableBeanFactory 接口表示这些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是这些Bean 是有继承关系的,也就是每个Bean 有可能有父 Bean。BeanAutowireCapableBeanFactory 接口定义Bean 的自动装配规则。
② 第二条设计主线是以ApplicationContext应用上下文接口为核心的接口设计。
这里涉及的主要设计接口有,从BeanFactory到ListableBeanFactory,再到ApplicationContext,再到常用的WebApplicationContext或者ConfigurableApplicationContext接口。我们常用的应用上下文基本都是ConfigurableApplicationContext或者WebApplicationContext的实现。在这个接口体系中,ListableBeanFactory和HiearerchialBeanFactory两个接口,连接BeanFactory接口定义和ApplicationContext应用上下文接口定义`。
在ListableBeanFactory接口中,细化了许多BeanFactory的接口功能,比如定义了getBeanDefinitionNames接口方法。
对于HierarchicalBeanFactory接口,我们在前面已经提到过其在继承了BeanFactory的基本接口之后,增加了getParentBeanFactory()的接口功能,使BeanFactory具备了双亲IOC容器的管理功能。
对于ApplicationContext接口,它通过继承了MessageSource、ResourceLoader、ApplicationEventPublisher接口,在BeanFactory简单Ioc容器的基础上添加了许多对高级容器的特性的支持。
图中涉及的是主要的接口关系,而具体的Ioc容器都是在这个接口体系下实现的,比如DefaultListableBeanFactory,这个基本Ioc容器的实现就是实现了ConfigurableBeanFactory,从而成为了一个简单Ioc容器的实现。像其他的Ioc容器,比如XMLBeanFactory,都是在DefaultListableBeanFactory的基础上做扩展。同样,ApplicationContext的实现也是如此。
这个接口系统是以BeanFactory和ApplicationContext为核心的,而BeanFactory又是Ioc容器的最基本的接口。在ApplicationContext的设计中,一方面,可以看到它继承了BeanFactory接口体系中的ListableBeanFactory、AutowireCapableBeanFactory、HiearerchialBeanFactory等BeanFactory的接口,具备了BeanFactory Ioc容器的基本功能。
另外一方面,通过继承MessageSource、ResourceLoader、ApplicationEventPublisher这些接口,BeanFactory为ApplicationContext赋予了更高级的Ioc容器特性。对于ApplicationContext而言,为了在Web环境中使用它,还设计了WebApplicationContext接口,而这个接口通过继承ThemeSource接口来扩充功能。
【3】BeanFactory
BeanFactory接口定义了IOC容器最基本的形式,并且提供了IOC容器所应该遵守的最基本的服务契约。BeanFactory接口设计了getBean方法,这个方法是使用IOC容器API的主要方法,通过这个方法,可以取得IOC容器中管理的Bean,Bean的取得是通过指定名字来索引的。如果需要在获取Bean时对Bean的类型进行检查,BeanFactory接口定义了带有参数的getBean方法,这个方法的使用与不带参数的getBean方法类似,不同的是增加了对Bean检索的类型的要求。
有了BeanFactory的定义,用户可以执行以下操作:
通过接口方法containsBean让用户能够判断容器是否含有指定名字的Bean;
通过接口方法isSingleton来查询指定名字的Bean是否是Singleton类型的Bean。对于Singleton属性,用户可以在BeanDefinition中指定;
通过接口方法isPrototype来查询指定名字的Bean是否是prototype类型的。与Singleton属性一样,这个属性也可以由用户在BeanDefinition中指定;
通过接口方法isTypeMatch来查询指定了名字的Bean的Class类型是否是特定的Class类型。这个Class类型可以由用户来指定。
通过接口方法getType来查询指定名字的Bean的Class类型;
通过接口方法getAliases来查询指定了名字的Bean的所有别名,这些别名都是用户在BeanDefinition中定义的。
BeanFactory接口源码
public interface BeanFactory { // 用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,如果一个FactoryBean的名字为 //a,那么,&a会得到那个Factory String FACTORY_BEAN_PREFIX = "&"; //五个不同形式的getBean方法,获取实例 Object getBean(String name) throws BeansException; //根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。 <T> T getBean(String name, Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType); <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType); // 判断bean是否存在 boolean containsBean(String name); // bean是否单例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // Bean是否为原型(多实例) boolean isPrototype(String name) throws NoSuchBeanDefinitionException; // 名称、类型是否匹配 boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; // 名称、类型是否匹配 boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException; // 获取类型 @Nullable Class<?> getType(String name) throws NoSuchBeanDefinitionException; @Nullable Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException; // 根据实例名字获取别名 String[] getAliases(String name); }
可以看到,这里定义的只是一系列的接口方法,通过这一系列的BeanFactory接口,可以使用不同的Bean的检索方法,很方便地从IOC容器中得到需要的Bean,从而忽略具体的IOC容器的实现。
也就是说在BeanFactory里只对IOC容器的基本行为作了定义,根本不关心你的bean是如何定义怎样加载的。正如我们只关心工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心。
【4】BeanFactory容器的设计原理
BeanFactory接口提供了使用IOC容器的规范,在这个基础上,Spring还提供了符合这个IOC容器接口的一系列容器的实现供开发人员使用,这里以XmlBeanFactory的实现为例来说明简单IOC容器的设计原理。
① XmlBeanFactory类继承关系
可以看到,作为一个简单IOC容器系列最底层实现的XmlBeanFactory,与我们在Spring应用中用到的那些上下文相比,有一个非常明显的特点:它只提供最基本的IOC容器的功能。理解这一点有助于我们理解ApplicationContext与基本的BeanFactory之间的区别和联系。
我们可以认为之间的BeanFactory实现是IOC容器的基本形式,而各种ApplicationContext的实现是IOC容器的高级表现形式。
在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IOC容器来使用的。XmlBeanFactory在继承了DefaultListableBeanFactory容器的功能的同时,增加了新的功能–可以读取以XML文件方式定义的BeanDefinition。
在XmlBeanFactory中,初始化了一个XMLBeanDefinitionReader对象,有了这个Reader对象,那些以XML方式定义的BeanDefinition就有了处理的地方。构造XmlBeanFactory这个IOC容器时,需要指定BeanDefinition的信息来源,这个信息来源需要封装成Spring中的Resource类来给出。
Resource是Spring用来封装IO操作的类。比如我们的BeanDefinition信息是以XML文件形式存在的,那么可以使用像ClassPathResource res=new ClassPathResource("beans.xml");这样具体的ClassPathResource来构造需要的Resource,然后将Resource作为构造参数传递给XmlBeanFactory构造函数。这样IOC容器就可以方便地定位到需要的BeanDefinition信息来对Bean完成容器的初始化和依赖注入过程。
XmlBeanFactory源码如下:
public class XmlBeanFactory extends DefaultListableBeanFactory { //这里初始化了一个XmlBeanDefinitionReader对XML形式的信息进行处理 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); // 使用给定的resource实例化XmlBeanFactory public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } }
综合来看,XmlBeanFactory的功能是建立在DefaultListableBeanFactory这个基本容器的基础上的,并在这个基本容器的基础上实现了其他诸如XML读取的附加功能。
如上源码所示,在XmlBeanFactory构造方法中需要得到Resource对象。对XmlBeanDefinitionReader对象的初始化以及使用这个对象来完成对loadBeanDefinitions的调用,就是这个调用启动从Resource中载入BeanDefinitions的过程,loadBeanDefinitions同时也是IOC容器初始化的重要组成部分。
我们看到XMLBeanFactory使用了DefaultListableBeanFactory作为基类,DefaultListableBeanFactory是很重要的一个IOC实现,在其他IOC容器中,比如ApplicationContext,其实现的基本原理和XMLBeanFactory一样,也是通过持有或者扩展DefaultListableBeanFactory来获得基本的IOC容器的功能的。
② 编程式使用IOC容器
//根据Xml配置文件创建Resource资源对象,该对象中包含了BeanDefinition的信息 ClassPathResource resource =new ClassPathResource("application-context.xml"); //创建DefaultListableBeanFactory DefaultListableBeanFactory factory =new DefaultListableBeanFactory(); //创建XmlBeanDefinitionReader读取器,用于载入BeanDefinition。之所以需要BeanFactory作为 //参数,是因为会将读取的信息回调配置给factory XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory); //XmlBeanDefinitionReader执行载入BeanDefinition的方法,最后会完成Bean的载入和注册。完成 //后Bean就成功的放置到IOC容器当中,以后我们就可以从中取得Bean来使用 reader.loadBeanDefinitions(resource);
这样,我们就可以通过factory对象来使用DefaultListableBeanFactory这个IOC容器了。在使用IOC 容器时,需要如下几个步骤:
创建IOC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息。
创建一个BeanFactory,这里使用DefaultListableBeanFactory。
创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入xml文件形式的BeanDefinition,通过一个回调配置给BeanFactory。
从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入和注册Bean定义之后,需要的IOC容器就建立起来了。这个时候就可以直接使用IOC容器了。
以编程 的方式使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition。这时使用的是ClassPathResource,这意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition信息。
ClassPathResource resource =new ClassPathResource("application-context.xml");
这里定义的Resource并不能由DefaultListableBeanFactory直接使用,Spring通过使用BeanDefinitionReader来对这些信息进行处理。在这里,我们也可以看到使用ApplicationContext相对于直接使用DefaultListableBeanFactory的好处。因为在ApplicationContext中,Spring已经为我们提供了一系列加载不同Resource的读取器的实现,而DefaultListableBeanFactory只是一个纯粹的IOC容器,需要为它配置特定的读取器才能完成这些功能。
【5】ApplicationContext
① 概述
在Spring中,系统已经为用户提供了许多已经定义好的容器实现,而不需要开发人员事必躬亲。相比那些简单拓展BeanFactory的基本IOC容器,开发人员常用的ApplicationContext除了能够提供前面介绍的容器的基本功能外,还为用户提供了一些附加服务,所以说ApplicationContext是一个高级形态意义的IOC容器。
ApplicationContext接口继承图
附加功能主要如下:
支持不同的信息源。我们看到ApplicationContext扩展了MessageSource接口,这些信息源的扩展功能可以支持国际化的实现,为开发多语言版本的应用提供服务。
访问资源。这一特性体现在对ResourceLoader和Resource的支持上,这样我们可以从不同地方得到Bean定义资源。这种抽象使用户程序可以灵活地定义Beean定义信息,尤其是从不同的IO途径得到Bean定义信息。一般来说,具体ApplicationContext都是继承了DefaultResourceLoader的子类。因为DefaultResourceLoader是AbstractApplicationContext的基类。
支持应用事件。继承了接口ApplicationEventPublisher,从而在上下文中引入了事件机制。这些事件和Bean的生命周期的结合为Bean的管理提供了便利。
在ApplicationContext中提供的附加服务。这些服务使得基本IOC容器的功能更丰富。因为具备了这些丰富的附加功能,使得ApplicationContext与简单的BeanFactory相比,对它的使用是一种面向框架的使用风格。
在ApplicationContext中Spring已经为我们提供了一系列加载不同Resource的读取器的实现,而类似DefaultListableBeanFactory只是一个纯粹的IOC容器,需要为它配置特定的读取器才能完成这些功能。当然是用DefaultListableBeanFactory这种更底层的容器,能提高定制IOC容器的灵活性。
② ApplicationContext容器的设计原理
这里以FileSystemXmlApplicationContext
的实现为例来说明ApplicationContext
容器的设计原理。
在FileSystemlXmlApplicationContext的设计中,我们看到ApplicationContext应用上下文的主要功能已经在FileSystemXmlApplicaitonContext的基类AbstractXmlApplicationContext中实现了,在FileSystemXmlApplicationContext中,作为一个具体的应用上下文,只需要实现和它自身设计相关的两个功能。
一个功能是,如果应用直接使用FileSystemlXmlApplicationContext,对于实例化这个应用上下文的支持,同时启动IOC容器的refresh()过程。代码如下所示:
public FileSystemXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
这个refresh()过程会牵涉IOC容器启动的一系列复杂操作,同时对于不同的容器实现,这些操作都是类似的,因此在基类中将它们封装好。所以我们在FileSystemlXmlApplicationContext的设计中看到的只是一个简单的调用。
另一个功能是与FileSystemlXmlApplicationContext设计具体相关的功能,这部分与怎样从文件系统中加载XML的Bean定义资源有关。
通过这个过程,可以为在文件系统中读取以XML形式存在的BeanDefinition做准备,因为不同的应用上下文实现对应着不同的读取BeanDefinition的方式,在FileSystemlXmlApplicationContext中的实现代码如下:
protected Resource getResourceByPath(String path) { if (path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); }
可以看到,调用这个方法,可以得到FileSystemResource的资源定位。
【6】BeanDefinition
在这些spring提供的基本IOC容器的接口定义和实现的基础上,spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及他们之间的相互依赖关系。BeanDefinition抽象了我们对Bean的定义,是让容易起作用的主要数据类型。
我们都知道,在计算机世界里,所有的功能都是建立在通过数据对现实进行抽象的基础上的。IOC容器是用来管理对象依赖关系的,对IOC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕着这个BeanDefinition的处理来完成的。
SpringIOC容器管理了我们定义的各种Bean对象及其相互的关系,Bean对象在Spring实现中是以BeanDefinition来描述的,其继承体系如下:
Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析,这个解析过程主要由如下类完成:
BeanDefinitionReader是简单的bean定义读取器,我们常用的默认实现是XmlBeanDefinitionReader,格式化读取xml bean定义。其通常将实际读取动作委派给BeanDefinitionDocumentReader接口的实现,如DefaultBeanDefinitionDocumentReader。
BeanDefinitionDocumentReader 接口源码如下所示,其只有一个方法registerBeanDefinitions用来从给定的DOM 文档中读取bean定义并注册到给定的上下文中。
public interface BeanDefinitionDocumentReader { void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) throws BeanDefinitionStoreException; }
【7】IOC容器的初始化过程-引言
简单来说,IOC容器的初始化是由前面介绍的refresh()方法来启动的,这个方法标志着IOC容器的正式启动。具体来说,这个启动包括BeanDefinition的Resource定位、载入和注册三个基本过程。Spring把这三个过程分开,并使用不同的模块来完成,如使用相应的 ResourceLoader、BeanDefinitionReader等模块。通过这样的设计方式,可以让用户更加灵活地对这三个过程进行剪裁或扩展,定义出最适合自己的IOC容器的初始化过程。
① 第一个过程是Resource定位过程
这个Resource定位指的是BeanDefinition的资源定位。它由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一接口。
对于这些BeanDefinition的存在形式,相信大家都不会感到陌生。比如在文件系统中的Bean定义信息可以使用FileSystemResource来进行抽象;在类路径的Bean定义信息可以使用前面提到的ClassPathResource来使用等等。这个定位过程类似于容器寻找数据的过程。
② 第二个过程是BeanDefinition的载入
这个载入过程就是把用户定义好得多Bean表示成IOC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。
具体来说,这个BeanDefinition实际上就是pojo对象在IOC容器中的抽象,通过这个BeanDefinition定义的数据结构,使IOC容器能够方便地对pojo对象也就是bean进行管理。
③ 第三个过程是向IOC容器注册这些BeanDefinition的过程
这个过程是通过调用BeanDefinitionRegistry接口的实现来完成的。这个注册过程把载入过程中解析得到的BeanDefinition向IOC容器进行注册。通过分析,我们可以看到,在IOC容器内部将BeanDefinition注入到一个HashMap中去,IOC容器就是通过这个HashM来持有这些BeanDefinition数据的。
值得注意的是,这里谈的是IOC容器的初始化过程,在这个过程中,一般不包含Bean依赖注入的实现。在Spring IOC的设计中,Bean定义的载入和依赖注入是两个独立的过程。依赖注入一般发生在应用第一次通过getBean向容器索取Bean的时候。但有一个例外值得注意,在使用IOC容器时有一个预实例化的配置,通过这个预实例化的配置(具体来说就是Bean定义信息中的lazyinit属性),用户可以对容器初始化过程作一个微小的控制,从而改变这个被设置了lazyinit属性的Bean的依赖注入过程。
举例来说,如果我们对某个Bean设置了lazyinit属性,那么这个Bean的依赖注入在IOC容器初始化时就预先完成了,而不需要等到整个初始化完成以后,第一次使用getBean时才会触发。