BeanDefinition的载入和解析

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的载入

 1 public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
 2             throws BeansException {
 3
 4         super(parent);
 5         setConfigLocations(configLocations);
 6         // 这里调用容器的refresh,是载入BeanDefinition的入口
 7         if (refresh) {
 8             refresh();
 9         }
10     }

启动BeanDefinition的载入

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

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

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

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

代码清单2-8  对IoC容器执行refresh的过程

 1 public void refresh() throws BeansException, IllegalStateException {
 2         synchronized (this.startupShutdownMonitor) {
 3             // Prepare this context for refreshing.
 4             prepareRefresh();
 5
 6             // Tell the subclass to refresh the internal bean factory.
 7             // 这里是在子类中启动refreshBeanFactory()的地方
 8             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 9
10             // Prepare the bean factory for use in this context.
11             prepareBeanFactory(beanFactory);
12
13             try {
14                 // 设置BeanFactory的后置处理
15                 // Allows post-processing of the bean factory in context subclasses.
16                 postProcessBeanFactory(beanFactory);
17
18                 // 调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的
19                 // Invoke factory processors registered as beans in the context.
20                 invokeBeanFactoryPostProcessors(beanFactory);
21
22                 // 注册Bean的后处理器,在Bean创建过程中调用
23                 // Register bean processors that intercept bean creation.
24                 registerBeanPostProcessors(beanFactory);
25
26                 // 对上下文中的消息源进行初始化
27                 // Initialize message source for this context.
28                 initMessageSource();
29
30                 // 初始化上下文中的事件机制
31                 // Initialize event multicaster for this context.
32                 initApplicationEventMulticaster();
33
34                 // 初始化其他的特殊Bean
35                 // Initialize other special beans in specific context subclasses.
36                 onRefresh();
37
38                 // 检查监听Bean并且将这些Bean向容器注册
39                 // Check for listener beans and register them.
40                 registerListeners();
41
42                 // 实例化所有的(non-lazy-init)单件
43                 // Instantiate all remaining (non-lazy-init) singletons.
44                 finishBeanFactoryInitialization(beanFactory);
45
46                 // 发布容器事件,结束Refresh过程
47                 // Last step: publish corresponding event.
48                 finishRefresh();
49             }
50
51             catch (BeansException ex) {
52                 logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
53
54                 // 为防止Bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件Bean
55                 // Destroy already created singletons to avoid dangling resources.
56                 destroyBeans();
57
58                 // 重置 ‘active‘标志
59                 // Reset ‘active‘ flag.
60                 cancelRefresh(ex);
61
62                 // Propagate exception to caller.
63                 throw ex;
64             }
65         }
66     }

对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方法

 1 protected final void refreshBeanFactory() throws BeansException {
 2         // 这里判断,如果已经建立了BeanFactory,则销毁并关闭该BeanFactory
 3         if (hasBeanFactory()) {
 4             destroyBeans();
 5             closeBeanFactory();
 6         }
 7         // 这里是创建并设置持有的DefaultListableBeanFactory的地方
 8         // 同时调用loadBeanDefinitions载入BeanDefinition的信息
 9         try {
10             DefaultListableBeanFactory beanFactory = createBeanFactory();
11             beanFactory.setSerializationId(getId());
12             customizeBeanFactory(beanFactory);
13             loadBeanDefinitions(beanFactory);
14             synchronized (this.beanFactoryMonitor) {
15                 this.beanFactory = beanFactory;
16             }
17         }
18         catch (IOException ex) {
19             throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
20         }
21     }

AbstractRefreshableApplicationContext的refreshBeanFactory方法



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

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

 1 public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
 2
 3     /**
 4      * Create a new AbstractXmlApplicationContext with no parent.
 5      */
 6     public AbstractXmlApplicationContext() {
 7     }
 8
 9     /**
10      * Create a new AbstractXmlApplicationContext with the given parent context.
11      * @param parent the parent context
12      */
13     public AbstractXmlApplicationContext(ApplicationContext parent) {
14         super(parent);
15     }
16
17 }

AbstractXmlApplicationContext的loadBeanDefinitions

 1 // 这里是实现loadBeanDefinitions的地方
 2     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
 3         // Create a new XmlBeanDefinitionReader for the given BeanFactory.
 4         // 创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中去,创建BeanFactory
 5         // 的过程可以参考上文对编程式使用IoC容器的相关分析,这里和前面一样,使用的也是
 6         // DefaultListableBeanFactory
 7         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
 8
 9         // Configure the bean definition reader with this context‘s
10         // resource loading environment.
11         // 这里设置XmlBeanDefinitionReader,为XmlBeanDefinitionReader配
12         // ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用
13         beanDefinitionReader.setEnvironment(this.getEnvironment());
14         beanDefinitionReader.setResourceLoader(this);
15         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
16
17         // Allow a subclass to provide custom initialization of the reader,
18         // then proceed with actually loading the bean definitions.
19         // 这是启动Bean定义信息载入的过程
20         initBeanDefinitionReader(beanDefinitionReader);
21         loadBeanDefinitions(beanDefinitionReader);
22     }
23
24     protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
25         reader.setValidating(this.validating);
26     }

loadBeanDefinitions

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

代码清单2-11 XmlBeanDefinitionReader载入BeanDefinition

 1 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
 2         // 以Resource的方式获得配置文件的资源位置
 3         Resource[] configResources = getConfigResources();
 4         if (configResources != null) {
 5             reader.loadBeanDefinitions(configResources);
 6         }
 7         // 以String的形式获得配置文件的定位
 8         String[] configLocations = getConfigLocations();
 9         if (configLocations != null) {
10             reader.loadBeanDefinitions(configLocations);
11         }
12     }
13
14     protected Resource[] getConfigResources() {
15         return null;
16     }

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

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

AbstractBeanDefinitionReader载入BeanDefinition

时间: 2024-12-21 01:29:59

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

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

在前文提过,IOC 容器的初始化过程分为三步骤:Resource 定位.BeanDefinition 的载入和解析,BeanDefinition 注册. Resource 定位.我们一般用外部资源来描述 Bean 对象,所以在初始化 IOC 容器的第一步就是需要定位这个外部资源.BeanDefinition 的载入和解析.装载就是 BeanDefinition 的载入.BeanDefinitionReader 读取.解析 Resource 资源,也就是将用户定义的 Bean 表示成 IOC 容器

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

报错信息:Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/p], 一桩事故引发的连锁思考...开幕—— ----------------------------------------------------------------------------------------------

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

Android异步载入全解析之大图处理 异步载入中很重要的一部分就是对图像的处理,这也是我们前面用异步载入图像做示例的原因. 一方面是由于图像处理不好的话会很占内存,并且easyOOM,还有一方面,图像也比文字要大,载入比較慢.所以,在解说了怎样进行多线程.AsyncTask进行多线程载入后,先暂停下后面的学习.来对图像的异步处理进行一些优化工作. 为什么要对图像处理 为什么要对图像进行处理,这是一个很直接的问题.一张图像.无论你拿手机.相机.单反还是什么玩意拍出来,它就有一定的大小,可是在不同

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

异步载入之使用多线程 初次尝试 异步.异步,事实上说白了就是多任务处理.也就是多线程执行.多线程那就会有各种问题,我们一步步来看.首先.我们创建一个class--ImageLoaderWithoutCaches,从命名上.大家也看出来,这个类,我们实现的是不带缓存的图像载入,不多说,我们再创建一个方法--showImageByThread,通过多线程来载入图像: /** * Using Thread * @param imageView * @param url */ public void s

Android异步载入全解析之IntentService

Android异步载入全解析之IntentService 搞什么IntentService 前面我们说了那么多,异步处理都使用钦定的AsyncTask.再不济也使用的Thread,那么这个IntentService是个什么鬼. 相对与前面我们提到的这两种异步载入的方式来说.IntentService有一个最大的特点.就是--IntentService不受大部分UI生命周期的影响.它为后台线程提供了一个更直接的操作方式.只是,IntentService的不足主要体如今下面几点: 不能够直接和UI做

Android异步载入全解析之开篇瞎扯淡

Android异步载入 概述 Android异步载入在Android中使用的很广泛,除了是由于避免在主线程中做网络操作.更是为了避免在显示时由于时间太长而造成ANR,添加显示的流畅性,特别是像ListView.GridView这种控件.假设getView的时间太长,就会造成很严重的卡顿,很影响性能. 本系列将展示在Android中怎样进行异步载入操作,并使用ListView来作为演示的对象. 怎样下载图像 下载自然是须要使用网络,使用网络就不能在主线程.在主线程就会爆炸.所以我们必须要在非主线程

BeanDefinition的Resource定位——3

1.我们重点看看AbstractRefreshableApplicationContext的refreshBeanFactory方法的实现,这个refreshBeanFactory被FileSystemXmlApplicationContext构造函数中的refresh调用.在这个方法中,通过createBeanFactory构建了一个IoC容器供ApplicationContext使用.这个IoC容器就是我们前面提到过的DefaultListableBeanFactory,同时,它启动了loa

SPRING源码解析-SPRING 核心-IOC

IoC 和 AOP是Spring的核心, 是Spring系统中其他组件模块和应用开发的基础.透过这两个模块的设计和实现可以了解Spring倡导的对企业应用开发所应秉承的思路: 易用性. POJO开发企业应用, 直接依赖于Java语言,而不是容器和框架. 提升程序的可测试性,提高软件质量. 提供一致性编程模型,面向接口的编程 降低应用的负载和框架的侵入性.IoC和AOP实现. 不作为现有解决方案的替代,而是集成现有. IoC和AOP这两个核心组件,特别是IoC容器,使用户在使用Spring完成PO

Spring技术内幕——Spring Framework的IOC容器实现(二)

三.IOC容器的初始化过程 IOC容器的初始化时由前面介绍的refresh方法来启动的,这个方法标志着IOC容器的正式启动.这个启动包括BeanDefinition的Resource定位.载入和注册.下面我们将详细分析这三个实现过程,Spring把这三个过程分开,并使用不同的模块来完成,通过这样的设计让用户更加灵活的这三个过程进行剪裁和扩展,定义出最适合自己的IOC容器的初始化过程. 第一个过程: Resource定位过程,是指BeanDefinition的资源定位,他由ResourceLoad