目录
注册Spring自带的属性编辑器CustomDateEditor
添加ApplicationContextAwareProcess可处理器
初始化ApplicationEventMuliticaster
注意:本文摘自 spring源码深度解析
简介
经过前面几章的分析,相信大家已经对Spring中的容器功能有了简单的了解,在前面的章节中我们一直以BeanFactory接口以及它的默认实现类XmlBeanFactory为例进行分析,但是,Spring中还提供了另一个接口ApplicationContext, 用于扩展BeanFactory中现有的功能。
ApplicationContext和BeanFactory两者都是用于加载Bean的,但是相比之下,ApplicationContext提供了更多的扩展功能,简单一点说:ApplicationContext包含BeanFactory的所有功能。通常建议比BeanFactory优先,除非在一些限制的场合,比如字节长度对内存有很大的影响时(Applet)。绝大多数”典型的“企业应用和系统,ApplicationContext就是你需要使用的。
那么究竟ApplicationContext比BeanFactory多出了哪些功能呢?还需要我们进一步的探索。首先我们来看看使用两个不同的类去加载配置文件在写法上的不同。
使用BeanFactory方式加载XML。
BeanFactory bf= new XmlBeanFactory(new ClassPathResource("BeanFactoryTest.Xml"));
使用ApplicationContext方式加载XML。
ApplicationContext bf= new ClassPathXmlApplicationContext("BeanFactoryTest.Xml");
同样,我们还是以ClassPathXmlApplicationContext作为切入点,开始对整体功能进行分析。
设置路径是必不可少的步骤,ClassPathXmlApplicationContext中可以将配置文件路径以数组的方式传入,ClassPathXmlApplicationContext可以对数组进行解析并进行加载。而对于解析及功能实现都在refresh()中实现。
设置配置路径
在ClassPathXmlApplicationContext中支持多个配置文件以数组方式同时传入:

此函数主要用于解析给定的路径数组,当然,如果数组中包含特殊符号,如${var},那么在resolvePath中会搜寻匹配的系统变量并替换。
扩展功能
设置了路径之后,便可以根据路径做配置文件的解析以及各种功能的实现了。可以说refresh函数中包含了几乎ApplicationContext中提供的全部功能,而且此函数中逻辑非常清晰明了,使我们很容易分析对应的层次及逻辑。
下面概括一下ClassPathXmlApplicationContext初始化的步骤,并从中解释一下它为我们提供的功能。
1 初始化前的准备工作,例如对系统属性或者环境变量进行准备及验证。
在某种情况下项目的使用需要读取某些系统变量,而这个变量的设置很可能会影响着系统的正确性,那么ClassPathXmlApplicationContext为我们提供的这个准备函数就显得非常必要,它可以在Spring启动的时候提前对必需的变量进行存在性验证。
2. 初始化BeanFactory,并进行XML文件读取。
之前有提到ClassPathXmlApplicationContext包含着BeanFactory所提供的一切特征,那么在这一步骤中将会复用BeanFactory中的配置文件读取解析及其他功能,这一步之后,ClassPathXmlApplicationContext实际上就已经包含了BeanFactory所提供的功能,也就是可以进行bean的提取等基础操作了。
3 对BeanFactory进行各种功能填充。
@Qualifier与@Autowired应该是大家非常熟悉的注解,那么这两个注解正是在这一步骤中增加的支持。
4. 子类覆盖方法做额外的处理。
Spring之所以强大,为世人所推崇,除了它功能上为大家提供了便例外,还有一方面是它的完美架构,开放式的架构让使用它的程序员很容易根据业务需要扩展已经存在的功能。这种开放式的设计在Spring中随处可见,例如在本例中就提供了一个空的函数实现postProcessBeanFactory来方便程序员在业务上做进一步扩展。
5 激活各种BeanFactory处理器。
6 注册拦截bean创建的bean处理器,这里只是注册,真正的调用是在getBean时候。
7 为上下文初始化Message源,即对不同语言的消息体进行国际化处理。
8 初始化应用消息广播器,并放入"applicationEventMulticaster"bean中。
9. 留给子类来初始化其他的bean。
10 在所有注册的bean中查找listener bean,注册到消息广播器中。
11 初始化剩下的单实例(非惰性的)。
12 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人。
环境准备
prepareRefresh函数主要是做些准备工作,例如对系统属性及环境变量的初始化及验证。
网上有入说其实这个函数没什么用,因为最后两句代码才是最为关键的,但是却没有什么逻辑处理,initPropertySources是空的,没有任何逻辑,而getEnvironment().validateRequiredProperties也因为没有需要验证的属性而没有做任何处理。其实这都是因为没有彻底理解才会这么说,这个函数如果用好了作用还是挺大的。那么,该怎么用呢?我们先探索下各个函数的作用。
1. initPropertySources正符合Spring的开放式结构设计,给用户最大扩展Spring的能力。
用户可以根据自身的需要重写initPropertySources方法,并在方法中进行个性化的属性处理及设置。
2. validateRequiredProperties则是对属性进行验证,那么如何验证呢?我们举个融合两句代码的小例子来帮助大家理解。
假如现在有这样一个需求,工程在运行过程中用到的某个设置(例如VAR)是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,那么工程可能不会工作。这一要求可能会有各种各样的解决办法,当然,在Spring中可以这样做,你可以直接修改Spring的源码,例如修改ClassPathXmlApplicationContext。当然,最好的办法还是对源码进行扩展,我们可以自定义类:
我们自定义了继承自ClassPathXmlApplicationContext的MyClassPathXmlApplicationContext,并重写了initPropertySources方法,在方法中添加了我们的个性化需求,那么在验证的时候也就是程序走到getEnvironment(). validateRequiredProperties()代码的时候,如果系统并没有检测到对应VAR的环境变量,那么将抛出异常。当然我们还需要在使用的时候替换掉原有的ClassPathXmlApplicationContext:
加载BeanFactory
obtainFreshBeanFactory方法从字面理解是获取BeanFactory。之前有说过,ApplicationContext是对BeanFactory的功能上的扩展,不但包含了BeanFactory的全部功能更在其基础上添加了大量的扩展应用,那么obtainFreshBeanFactory正是实现BeanFactory的地方,也就是经过了这个函数后ApplicationContext就已经拥有了BeanFactory的全部功能。
我们详细分析上面的每个步骤。
I. 创建DefaultListableBeanFactory。
在介绍BeanFactory的时候,不知道读者是否还有印象,声明方式为: BeanFactory bf= newXmlBeanFactory("beanFactoryTest.Xml"),其中的XmlBeanFactory继承自DefaultListableBeanFactory,并提供了XmlBeanDefinitionReader类型的reader属性,也就是说DefaultListableBeanFactory是容器的基础。必须首先要实例化,那么在这里就是实例化DefaultListableBeanFactory的步骤。
2 指定序列化ID。
3 定制BeanFactory。
4 加载BeanDefinition。
5 使用全局变量记录BeanFactory类实例。
因为DefaultListableBeanFactory类型的变量BeanFactory是函数内的局部变量,所以要使用全局变量记录解析结果。
定制BeanFactory
这里已经开始了对BeanFactory的扩展,在基本容器的基础上,增加了是否允许覆盖是否允许扩展的设置并提供了注解@Qualifier和@Autowired的支持。
对于允许覆盖和允许依赖的设置这里只是判断了是否为空,如果不为空要进行设置,但是并没有看到在哪里进行设置,究竟这个设置是在哪里进行设置的呢?还是那句话,使用子类覆盖方法,例如:
设置完后相信大家已经对于这两个属性的使用有所了解,或者可以回到前面的章节进行再一次查看。对于定制BeanFactory,Spring还提供了另外一个重要的扩展,就是设置AutowireCandidateResolver,在bean加载部分中讲解创建Bean时,如果采用AutowireByType方式注入,那么默认会使用Spring提供的SimpleAutowireCandidateResolver,而对于默认的实现并没有过多的逻辑处理。在这里,Spring使用了QualifierAnnotationAutowireCandidateResolver,设置了这个解析器后Spring就可以支持注解方式的注入了。
在讲解根据类型自定注入的时候,我们说过解析Autowire类型时首先会调用方法:
Ob