1.在完成对代表BeanDefinition的Resource定位的分析后,下面来了解整个BeanDefinition信息的载入过程。

2.对IoC容器来说,这个载入过程,相当于把定义的BeanDefinition在IoC容器中转化成一个Spring内部表示的数据结构的过程。

3.IoC容器对Bean的管理和依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关操作来完成的。

4.这些BeanDefinition数据在IoC容器中通过一个HashMap来保持和维护。当然这只是一种比较简单的维护方式,如果需要提高IoC容器的性能和容量,完全可以自己做一些扩展。

5.下面,从DefaultListableBeanFactory的设计入手,看看IoC容器是怎样完成BeanDefinition载入的。这个DefaultListableBeanDefinition在前面已经碰到多次,相信大家对它一定不会感到陌生。

6.在开始分析之前,先回到IoC容器初始化入口,也就是看一下refresh方法。这个方法的最初是在FileSystemXmlApplicationContext的构造函数中被调用的,它的调用标志着容器初始化的开始,这些初始化对象就是BeanDefinition数据,初始化入口如代码清单2-7所示。

代码清单2-7 启动BeanDefinition的载入

 public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException { super(parent);
setConfigLocations(configLocations);
// 这里调用容器的refresh,是载入BeanDefinition的入口
if (refresh) {
refresh();
}
}

启动BeanDefinition的载入

7.对容器的启动来说,refresh是一个很重要的方法,下面介绍一下他的实现。

8.该方法在AbstractApplicationContext类(它是FileSystemXmlApplicationContext的基类)中找到,它详细地描述了整个ApplicationContext的初始化过程,比如BeanFactory的更新,MessageSource和PostProcessor的注册,等等。

9.这里看起来更像是对ApplicationContext进行初始化的模块或执行提纲,这个执行过程为Bean的生命周期管理提供了条件。

10.熟悉IoC容器使用的读者,从这一系列调用的名字就能大致了解应用上下文初始化的主要内容。这里就直接列出代码,不做太多的解释了。这个IoC容器的refresh过程如代码清单2-8所示。

代码清单2-8  对IoC容器执行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.
// 这里是在子类中启动refreshBeanFactory()的地方
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// 设置BeanFactory的后置处理
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // 调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // 注册Bean的后处理器,在Bean创建过程中调用
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // 对上下文中的消息源进行初始化
// Initialize message source for this context.
initMessageSource(); // 初始化上下文中的事件机制
// Initialize event multicaster for this context.
initApplicationEventMulticaster(); // 初始化其他的特殊Bean
// Initialize other special beans in specific context subclasses.
onRefresh(); // 检查监听Bean并且将这些Bean向容器注册
// Check for listener beans and register them.
registerListeners(); // 实例化所有的(non-lazy-init)单件
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // 发布容器事件,结束Refresh过程
// Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex); // 为防止Bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件Bean
// Destroy already created singletons to avoid dangling resources.
destroyBeans(); // 重置 'active'标志
// Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
}
}
}

对IoC容器执行refresh的过程

1.进入到AbstractRefreshableAppliccationContext的refreshBeanFactroy()方法中,在这个方法中创建了BeanFactory。

2.在创建IoC容器前,如果已经有容器存在,那么需要把已有的容器销毁和关闭,保证在refresh以后使用的是新建立起来的IoC容器。

3.这么看来,这个refresh非常像启动容器,就像启动计算机那样。在建立好当前的IoC容器以后,开始了对容器的初始化,比如BeanDefinition的载入,具体的交互过程如图2-10所示。可以从AbstractRefreshableApplicationCotext的refreshBeanFactory方法开始,了解这个Bean定义信息载入的过程,具体实现如代码清单2-9所示。

代码清单2-9 AbstractRefreshableApplicationContext的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 bean definition source for " + getDisplayName(), ex);
}
}

AbstractRefreshableApplicationContext的refreshBeanFactory方法


图2-10 BeanDefinition载入中的交互过程

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

 public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {

     /**
* Create a new AbstractXmlApplicationContext with no parent.
*/
public AbstractXmlApplicationContext() {
} /**
* Create a new AbstractXmlApplicationContext with the given parent context.
* @param parent the parent context
*/
public AbstractXmlApplicationContext(ApplicationContext parent) {
super(parent);
} }

AbstractXmlApplicationContext的loadBeanDefinitions

 // 这里是实现loadBeanDefinitions的地方
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中去,创建BeanFactory
// 的过程可以参考上文对编程式使用IoC容器的相关分析,这里和前面一样,使用的也是
// DefaultListableBeanFactory
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(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(new ResourceEntityResolver(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

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

代码清单2-11 XmlBeanDefinitionReader载入BeanDefinition

 protected void loadBeanDefinitions(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;
}

XmlBeanDefinitionReader载入BeanDefinition

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

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

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

代码清单2-12  AbstractBeanDefinitionReader载入BeanDefinition

 // AbstractBeanDefinitionReader载入BeanDefinition
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
// 如果Resource为空,则停止BeanDefinition的载入
// 然后启动载入BeanDefinition的过程,这个过程会遍历整个Resource集合所
// 包含的BeanDefinition信息
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
counter += loadBeanDefinitions(resource);
}
return counter;
}

AbstractBeanDefinitionReader载入BeanDefinition

BeanDefinition的载入和解析的更多相关文章

  1. Spring IOC容器的初始化-(二)BeanDefinition的载入和解析

    前言 1.在讲BeanDefinition的载入和解析之前,我们先来看看什么是BeanDefinition. Bean对象在Spring中是以BeanDefinition来描述的,也就是说在Sprin ...

  2. Resource 定位、BeanDefinition 的载入和解析,BeanDefinition 注册。

    在前文提过,IOC 容器的初始化过程分为三步骤:Resource 定位.BeanDefinition 的载入和解析,BeanDefinition 注册. Resource 定位.我们一般用外部资源来描 ...

  3. Spring源码解析(三)BeanDefinition的载入、解析和注册

    通过上一篇源码的分析已经完成了BeanDefinition资源文件的定位,本篇继续分析BeanDefinition资源文件的载入和解析. AbstractBeanDefinitionReader的lo ...

  4. (spring-第7回【IoC基础篇】)BeanDefinition的载入与解析&&spring.schemas、spring.handlers的使用

    报错信息:Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http: ...

  5. Spring-IOC源码解读2.2-BeanDefinition的载入和解析过程

    1. 对IOC容器来说这个载入过程就像是相当于把BeanDefinition定义的信息转化成Spring内部表示的数据结构.容器对bean的管理和依赖注入过程都是通过对其持有的BeanDefiniti ...

  6. Android异步载入全解析之使用多线程

    异步载入之使用多线程 初次尝试 异步.异步,事实上说白了就是多任务处理.也就是多线程执行.多线程那就会有各种问题,我们一步步来看.首先.我们创建一个class--ImageLoaderWithoutC ...

  7. Android异步载入全解析之IntentService

    Android异步载入全解析之IntentService 搞什么IntentService 前面我们说了那么多,异步处理都使用钦定的AsyncTask.再不济也使用的Thread,那么这个Intent ...

  8. Android异步载入全解析之大图处理

    Android异步载入全解析之大图处理 异步载入中很重要的一部分就是对图像的处理,这也是我们前面用异步载入图像做示例的原因. 一方面是由于图像处理不好的话会很占内存,并且easyOOM,还有一方面,图 ...

  9. Android异步载入全解析之使用AsyncTask

    Android异步载入全解析之使用AsyncTask 概述 既然前面提到了多线程,就不得不提到线程池,通过线程池,不仅能够对并发线程进行管理.更能够提高他们运行的效率.优化整个App.当然我们能够自己 ...

随机推荐

  1. 设备\Device\Harddisk1\DR1 有一个不对的区块

    近期遇到一个windows上的Oracle DB system表空间有问题.然后第一个反应就是查看windows的日志查看器,确实发现了报错: 设备\Device\Harddisk1\DR1 有一个不 ...

  2. spring mvc 3.0 ModelAndView模型视图类

    见名知意,从名字上我们可以知道ModelAndView中的Model代表模型,View代表视图.即,这个类把要显示的数据存储到了Model属性中,要跳转的视图信息存储到了view属性.我们看一下Mod ...

  3. iOS xcode8提交 iOS10 “此构建版本无效” (已解决)

    近期上传应用,遇到了"此构建版本无效"的问题,如图 网查了一下,解决了这个问题:(注意:先不要急着怀疑是网络问题,重新提交,先检查问题,别问我怎么知道的...) 1:iOS10 之 ...

  4. iOS开发——UI篇Swift篇&UIDatePicker

    UIDatePicker //返回按钮事件 @IBAction func backButtonClick() { self.navigationController?.popViewControlle ...

  5. 使用javaScript解决asp.net中mvc使用ajax提交数组参数的匹配问题

    想到在asp.net的mvc中如果使用ajax向服务端传递参数时如果参数是一个类或者是个数组(或List集合)以及更复杂的对象时,服务端总是会发生取不到值的情况,当然网上也有很多解决的例子,但都是在服 ...

  6. Spring MVC 中 HandlerInterceptorAdapter的使用--转载

    原文地址:http://blog.csdn.net/liuwenbo0920/article/details/7283757 一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的,这种方式可 ...

  7. 使用docker-hub

    使用docker hub 需要其账号 基本操作 查找镜像 sudo docker search centos 每个用户有自己的命名空间,如:centos是存仓库中的镜像文件,admln/centos则 ...

  8. IIS 之 打开/关闭 Internet 信息服务

    由于建立测试网站测试代码,重装电脑后不知道IIS在哪打开.下面以windows7为例介绍,打开IIS管理器的简要步骤. 第一步.查找IIS 1.点击" 开始 "→" 控制 ...

  9. java笔记 chapter1 java是什么,能干什么,有什么,特点,开发环境

    一,java是什么 二,java能干什么 三,java有什么 四,java的特点 五,java的三大特性:虚拟机,垃圾回收和代码安全 六,构建JSE开发环境:下载安装jdk和配置环境变量 七,编写并运 ...

  10. js中的命名规范

    在实际开发中规范的命名,不仅方便自己查看,理解变量的实际意义,而且在团队开发中也能提高开发效率. 下面将介绍javascript中的变量的命名规范: 1)首先,变量名要有实际意义,不建议使用单个的字母 ...