第二篇:IoC容器的初始化

在介绍FileSystemXmlApplicationContext 的例子时有说到IoC容器的初始化由refresh()方法开始启动,此方法标志着IoC容器的启动[构造器中有此方法的容器]。细分的话,这个启动过程具体的可分为三部分:

第一部分:BeanDefinition的Resource定位

是指BeanDefinition(容器内部定义的bean的数据结构),它由ResourceLoader通过统一的Resource接口来完成,Resource对各种形式的BeanDefinition的使用都提供了统一的接口,比如文件系统中的Bean定义信息可以通过FileSystemResource进行抽象,类路径中的Bean定信息可以通过ClassPathResource来获取。

在介绍XmlBeanFactory时可以看到,使用DefaultListableBeanFactory时,首先要定义一个Resource(即ClassPathResourceres = new ClassPathResource("SpringBeans.xml");)这里定义的Resource不能由DefaultListableBeanFactory直接使用,而是通过初始化一个XmlBeanDefinitionReader,靠reader对象来处理,完成定位的。

再拿FileSystemXmlApplicationContext的例子与之比较,不难发现ApplicationContext相对于直接使用DefaultListBeanFactory有一个好处----在ApplicationContext中Spring已经为我们提供了一系列加载不同Resource的读取器的实现。具体的读取器是怎样实现的正是此部分研究的重点。

还是那句话IoC容器的初始化由refresh()方法开始启动,FileSystemXmlApplicationContext在构造器中调用了refresh方法,在refresh方法中会调用obtainFreshBeanFactory获取beanFactory,在obtainFreshBeanFactory中会调用refreshBeanFactory这一抽象方法,在此方法的实现过程中构造了DefaultListableBeanFactory,通过XmlBeanFactory的例子我们知道DefaultListableBeanFactory初始化一个XmlBeanDefinitionReader,靠reader对象来处理,完成定位的。

reader对象有了之后,该怎样读入呢?

XmlBeanFactory是这样

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

这里同样需要调用loadBeanDefinitions方法,但resource是通过调用重写getResourceByPath获得的。子类FileSystemXmlApplicationContext重写了此方法会返回FileSystemResource对象,其他类型的ApplicationContext会对应生成其他类型的Resource。

这个FileSystemXmlApplicationContext的Resource定位用一句话概括就是:refresh()之后,构造出XmlBeanDefinitionReader对象,在loadBeanDefinitions时根据FileSystemXmlApplicationContext重写getResourceByPath返回的以FileSystem方式存在的resource对象进行BeanDefinition的载入,在定位完成之后知识为载入提供了I/O操作条件,并未开始读入。

第二部分:BeanDefinition的载入和解析

这个过程就是把用户定义好的Bean表示成IoC容器内部的数据结构即BeanDefinition。首先需要知道IoC容器对Bean的管理和依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关操作来完成的。这些BeanDefinition数据在IoC容器中通过一个HashMap来保持和维护。当然这只是一种比较简单的维护方式,如果需要提高IoC容器的性能和容量,完全可以自己做一些扩展。

下面,从DefaultListableBeanFactory的设计入手,看看IoC容器是怎样完成BeanDefinition载入的。

首先是初始化的入口—refresh方法,该方法在FileSystemXmlApplicationContext的构造方法中被调用,在FileSystemXmlApplicationContext的基类AbstractApplicationContext中实现,它详细地描述了整个ApplicationContext的初始化过程,比如BeanFactory的更新,MessageSource和PostProcessor的注册等。这里看起来更像是对ApplicationContext进行初始化的模块或执行提纲,这个执行过程为Bean的生命周期管理提供了条件。

refresh实现源码:

public void refresh() throwsBeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {  // Prepare this context forrefreshing.  prepareRefresh();  // Tell the subclass to refresh the internal bean factory.  // 这里是在子类中启动refreshBeanFactory()的地方  ConfigurableListableBeanFactory beanF actory= obtainFreshBeanFactory();  // Prepare the bean factory for use in this context.  prepareBeanFactory(beanFactory);  try {      // 设置BeanFactory的后置处理    // Allows post-processing ofthe bean factory in context subclasses.    postProcessBeanFactory(beanFactory);    // 调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的    // Invoke factory processorsregistered as beans in the context.    invokeBeanFactoryPostProcessors(beanFactory);    // 注册Bean的后处理器,在Bean创建过程中调用    // Register bean processorsthat intercept bean creation.    registerBeanPostProcessors(beanFactory);    // 对上下文中的消息源进行初始化    // Initialize message sourcefor this context.    initMessageSource();    // 初始化上下文中的事件机制    // Initialize event multicasterfor this context.    initApplicationEventMulticaster();    // 初始化其他的特殊Bean    // Initialize other specialbeans in specific context subclasses.    onRefresh();    // 检查监听Bean并且将这些Bean向容器注册    // Check for listener beans andregister them.    registerListeners();    // 实例化所有的(non-lazy-init)单件    // Instantiate all remaining(non-lazy-init) singletons.    finishBeanFactoryInitialization(beanFactory);    // 发布容器事件,结束Refresh过程    // Last step: publish correspondingevent.    finishRefresh();   }catch (BeansException ex) {    logger.warn("Exceptionencountered during context initialization - cancelling refresh attempt",ex);    // 为防止Bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件Bean    // Destroy already createdsingletons to avoid dangling resources.    destroyBeans();    // 重置 'active'标志    // Reset 'active' flag.    cancelRefresh(ex);    // Propagate exception tocaller.    throw ex;   } }}

在ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();中会调用AbstractRefreshableApplicationContext中的refreshBeanFactory方法,在这个方法中创建了BeanFactory。在创建IoC容器前,如果已经有容器存在,那么需要把已有的容器销毁和关闭,保证在refresh以后使用的是新建立起来的IoC容器。在建好容器后开始BeanDefinition的载入工作。

具体的refreshBeanFactory实现:

protected final void refreshBeanFactory()throws BeansException {    // 这里判断,如果已经建立了BeanFactory,则销毁并关闭该BeanFactory  if (hasBeanFactory()) {    destroyBeans();    closeBeanFactory();  }  // 这里是创建并设置持有的DefaultListableBeanFactory的地方  // 同时调用loadBeanDefinitions载入BeanDefinition的信息  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 beandefinition source for " + getDisplayName(), ex);  } }

BeanDefinition的载入交互过程:

这里调用的loadBeanDefinitions实际上是一个抽象方法,那么实际的载入过程发生在哪里呢?我们看看前面提到的loadBeanDefinitions在AbstractRefreshableApplicationContext的子类AbstractXmlApplicationContext中的实现,在这个loadBeanDefinitions中,初始化了读取器XmlBeanDefinitionReader,然后把这个读取器在IoC容器中设置好(过程和编程式使用XmlBeanFactory是类似的),最后是启动读取器来完成BeanDefinition在IoC容器的载入。

// 这里是实现loadBeanDefinitions的地方protected void loadBeanDefinitions(DefaultListableBeanFactorybeanFactory) throws BeansException, IOException {    // Create a new XmlBeanDefinitionReader for the given BeanFactory.  // 创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中去,创建BeanFactory  // 的过程可以参考上文对编程式使用IoC容器的相关分析,这里和前面一样,使用的也是  // DefaultListableBeanFactory  XmlBeanDefinitionReader beanDefinitionReader = newXmlBeanDefinitionReader(beanFactory);  // Configure the bean definition reader with this context's  // resource loading environment.  // 这里设置XmlBeanDefinitionReader,为XmlBeanDefinitionReader配  // ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用  beanDefinitionReader.setEnvironment(this.getEnvironment());  beanDefinitionReader.setResourceLoader(this);  beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));  // Allow a subclass to provide custom initialization of the reader,  // then proceed with actually loading the bean definitions.  // 这是启动Bean定义信息载入的过程  initBeanDefinitionReader(beanDefinitionReader);  loadBeanDefinitions(beanDefinitionReader);}protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader){       reader.setValidating(this.validating);}

接着就是loadBeanDefinitions调用的地方,首先得到BeanDefinition信息的Resource定位,然后直接调用XmlBeanDefinitionReader来读取,具体的载入过程是委托给BeanDefinitionReader完成的。因为这里的BeanDefinition是通过XML文件定义的,所以这里使用XmlBeanDefinitionReader来载入BeanDefinition到容器中。

protected voidloadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException,IOException {    // 以Resource的方式获得配置文件的资源位置  Resource[] configResources = getConfigResources();  if (configResources != null) {      reader.loadBeanDefinitions(configResources);  }  // 以String的形式获得配置文件的定位  String[] configLocations = getConfigLocations();  if (configLocations != null) {    reader.loadBeanDefinitions(configLocations);  }}protected Resource[] getConfigResources() {  return null;}

通过以上对实现原理的分析,我们可以看到,在初始化FileSystemXmlApplicationContext的过程中是通过调用IoC容器的refresh来启动整个BeanDefinition的载入过程的,这个初始化是通过定义的XmlBeanDefinitionReader来完成的。同时,我们也知道实际使用的IoC容器是DefualtListableBeanFactory,具体的Resource载入在XmlBeanDefinitionReader读入BeanDefinition时实现

因为Spring可以对应不同形式的BeanDefinition。由于这里使用的是XML方式的定义,所以需要使用XmlBeanDefinitionReder。如果使用了其他的BeanDefinition方式,就需要使用其他种类的BeanDefinitionReder来完成数据的载入工作。

在XmlBeanDefinitionReader的实现中可以看到,是在reader.loadBeanDefinitions中开始进行BeanDefinition的载入的,而这时XmlBeanDefinitionReader的父类AbstractBean-Definition-Reader已经为BeanDefinition的载入做好了准备。

@Overridepublicint loadBeanDefinitions(Resource... resources) throwsBeanDefinitionStoreException {    Assert.notNull(resources,"Resource array must not be null");  intcounter = 0;  for(Resource resource : resources) {    counter+= loadBeanDefinitions(resource);  }  returncounter;}

这里调用的loadBeanDefinitions(resource);在AbstractBeanDefinitionReader中没有实现,它是一个接口方法,具体实现在XmlBeanDefinitionReader中。在拥有了文件对象后就可以按照spring的bean定义规则来对XML文档树进行解析了,如何得到的document对象,可以参考DefaultDocumentLoader。紧接着Spring的BeanDefinition如何按照bean语义进行解析转化成容器内部数据结构的,这部分在registerBeanDefinition(doc,resource)中完成。

loadBeanDefinitionsà doLoadBeanDefinitionsà registerBeanDefinitions

至于怎样详细转化的不再深入了。

第三部分:向IoC容器注册这些BeanDefinition

这个过程是通过BeanDefinitionRegistry接口把载入过程中解析得到的BeanDefinition项IoC容器注册,IoC容器内部将这些BeanDefinition注入到HashMap中,IoC容器就是通过这个hashmap来持有这些BeanDefinition数据的。可以看到HashMap定义在DefaultBeanFactory中。

private static final Map<String,Reference<DefaultListableBeanFactory>> serializableFactories =

new ConcurrentHashMap<String,Reference<DefaultListableBeanFactory>>(8);

具体的注册调用过程为:

从上图的调用关系可以看到registerBeanDefinition方法何时被调用。

注意:这个初始化过程不包括Bean依赖注入的实现,依赖注入一般发生在应用第一次通过getBean向容器索取Bean的时候,可以通过设置Bean的lazyinit属性使得bean的依赖注入在IoC容器初始化是就完成。