我们前面一直以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作为切入点,开始对整体功能进行分析。
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
设置路径是必不可少的步骤
,ClassPathXmlApplicationContext中可以将配置文件路径以数组的方式传入,ClassPathXmlApplicationContext可以对数组进行解析并进行加载。而对于解析及功能实现都在refresh()中实现。
1.设置配置路径
在ClassPathXmlApplicationContext中支持多个配置文件以数组方式同时传入:
public void setConfigLocations(String[] locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
2.扩展功能
设置了路径之后,便可以根据路径做配置文件的解析以及各种功能的实现了。可以说refresh函数中包含了几乎ApplicationContext中提供的全部功能,而且此函数中逻辑非常清晰明了,使我们很容易分析对应的层次及逻辑
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//准备刷新的上下文 环境
/*
* 初始化前的准备工作,例如对系统属性或者环境变量进行准备及验证。
* 在某种情况下项目的使用需要读取某些系统变量,而这个变量的设置很可能会影响着系统
* 的正确性,那么ClassPathXmlApplicationContext为我们提供的这个准备函数就显得非常必要,
* 它可以在Spring启动的时候提前对必须的变量进行存在性验证。
*/
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//初始化BeanFactory,并进行XML文件读取
/*
* ClassPathXMLApplicationContext包含着BeanFactory所提供的一切特征,在这一步骤中将会复用
* BeanFactory中的配置文件读取解析及其他功能,这一步之后,ClassPathXmlApplicationContext
* 实际上就已经包含了BeanFactory所提供的功能,也就是可以进行Bean的提取等基础操作了。
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//对beanFactory进行各种功能填充
/*
* @Qualifier与@Autowired等注解正是在这一步骤中增加的支持
*/
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//子类覆盖方法做额外处理
/*
* Spring之所以强大,为世人所推崇,除了它功能上为大家提供了便利外,还有一方面是它的
* 完美架构,开放式的架构让g使用它的程序猿很容易根据业务需要扩展已经存在的功能。这种开放式
* 的设计在Spring中随处可见,例如在本例中就提供了一个空的函数实现postProcessBeanFactory来
* 方便程序猿在业务上做进一步扩展
*/
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//激活各种beanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用实在getBean时候
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//为上下文初始化Message源,即不同语言的消息提,国际化处理
initMessageSource();
// Initialize event multicaster for this context.
//初始化应用消息广播器,并放入“applicationEventMulticaster”bean中
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//留给子类来初始化其它的Bean
onRefresh();
// Check for listener beans and register them.
//在所有注册的bean中查找Listener bean,注册到消息广播器中
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//初始化剩下的单实例(非惰性的)
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出
//ContextRefreshEvent通知别人
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
3.环境准备
prepareRefresh函数主要是做些准备工作,例如对系统属性及环境变量的初始化及验证
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
synchronized (this.activeMonitor) {
this.active = true;
}
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize any placeholder property sources in the context environment
//留给子类覆盖
initPropertySources();
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
}
这个函数看似没有社么用,因为最后两句代码才是关键,但是却没有什么逻辑处理,initPropertySources是空的,没有什么逻辑,而getEnvironment().validaterequiredProperties也因为没有需要验证的属性而没有做任何处理。其实这个函数用好了作用还是挺大的,那么该怎么用呢,我们先探索下各个函数的作用。
(1)initPropertySources证符合Spring的开放式结构设计,给用户最大扩展Spring的能力。用户可以根据自身的需要重写initPropertySourece方法,并在方法中进行个性化的属性处理及设置。
(2)validateRequiredProperties则是对属性进行验证,那么如何验证呢?举个融合两句代码的小例子来理解。
例如现在有这样一个需求,工程在运行过程中用到的某个设置(例如VAR)是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,,工程不会工作。这一要求也许有各种各样的解决办法,在Spring中可以这么做,可以直接修改Spring的源码,例如修改ClassPathXmlApplicationContext.淡然,最好的办法是对源码进行扩展,可以自定义类:
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext{
public MyClassPathXmlApplicationContext(String.. configLocations){
super(configLocations);
}
protected void initPropertySources(){
//添加验证要求
getEnvironment().setRequiredProterties("VAR");
}
}
自定义了继承自ClassPathXmlApplicationContext的MyClassPathXmlApplicationContext,并重写了initPropertySources方法,在方法中添加了个性化需求,那么在验证的时候也就是程序走到getEnvironment().validateRequiredProperties()代码的时候,如果系统并没有检测到对应VAR的环境变量,将抛出异常。当然我们还需要在使用的时候替换掉原有的ClassPathXmlApplicationContext:
public static void main(Stirng[] args){
ApplicationContext bf = new MyClassPathXmlApplicationContext("myTest.xml");
User user = (User)bf.getBean("testBean");
}
4.加载BeanFactory
obtainFreshBeanFactory方法从字面理解是获取beanFactory.ApplicationContext是对BeanFactory的扩展,在其基础上添加了大量的基础应用,obtainFreshBeanFactory正式实现beanFactory的地方,经过这个函数后ApplicationContext就有了BeanFactory的全部功能。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//初始化BeanFactory,并进行XML文件读取,并将得到的BeanFactory记录在当前实体的属性中
refreshBeanFactory();
//返回 当前实体的beanFactory属性
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
方法中将核心实现委托给了refreshBeanFactory:
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建DefaultListableBeanFactory
/*
* 以前我们分析BeanFactory的时候,不知道是否还有印象,声明方式为:BeanFactory bf =
* new XmlBeanFactory("beanFactoryTest.xml"),其中的XmlBeanFactory继承自DefaulltListableBeanFactory;
* 并提供了XmlBeanDefinitionReader类型的reader属性,也就是说DefaultListableBeanFactory是容器的基础。必须
* 首先要实例化。
*/
DefaultListableBeanFactory beanFactory = createBeanFactory();
//为了序列化指定id,如果需要的话,让这个BeanFactory从id反序列化到BeanFactory对象
beanFactory.setSerializationId(getId());
//定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖以及设置
//@Autowired和Qualifier注解解析器QualifierAnnotationAutowireCandidateResolver
customizeBeanFactory(beanFactory);
//加载BeanDefiniton
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
//使用全局变量记录BeanFactory实例。
//因为DefaultListableBeanFactory类型的变量beanFactory是函数内部的局部变量,
//所以要使用全局变量记录解析结果
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
1)定制BeanFactory
customizeBeanFactor方法在基本容器的基础上,增加了是否允许覆盖是否允许扩展的设置并提供了主机@Qualifierh额@Autowired支持。
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
//如果属性sllowBeanDefinitionOverring不为空,设置给beanFactory对象相应属性,
//此属性的含义:是否允许覆盖同名称的不同定义的对象
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//如果属性allowCircularReferences不为空,设置给beanFactory对象相应属性,
//此属性的含义:是否允许循环依赖
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
//用于@Qualifier和@Autowired
beanFactory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
}
对于允许覆盖和允许依赖的设置这里只判断了是否为空,如果不为空姚进行设置,但是并没有看到在哪里设置,究竟在哪里设置?
这时候还是利用Spring的可扩展性,使用子类覆盖的方法,例如:
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext{
... ...
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory){
super.setAllowBeanDefinitionOverriding(fa;se);
super.setAllowCircularReferences(false);
super.customizeBeanFactory(beanFactory);
}
}
Spring使用了QualifierAnnotationAutowireCandidateResolver这个解析器后,Spring就支持注解方式的注入了,
在以前分析根据类型注入的时候,我们说过解析autowire类型时首先会调用方法:
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
因此,在QualifierAnnotationAutowireCandidatResolver中一定会提供解析Autowire注解的方法:
public Object getSuggestedValue(DependencyDescriptor descriptor) {
Object value = findValue(descriptor.getAnnotations());
if (value == null) {
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
value = findValue(methodParam.getMethodAnnotations());
}
}
return value;
}
加载beanDefinition
在第一步中提到了将ClassPathXmlApplicationContext与XMLBeanFactory创建的对比,除了初始化DefaultListableBeanFactory外,还需要XmlBeanDefinitionReader来读取XML,那么在loadBeanDefinitions方法中首先要做的就是初始化XmlBeanDefinitonReader.
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
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.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
在初始化了DefaultListableBeanFactory和XmlBeanDefinitionReader后,就可以进行配置文件的读取了。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
因为在XmlBeanDefinitionReader中已经将之前初始化的DefaultListableBeanFactory注册进去了,所以XmlBeanDefinitionReader所读取的BeanDefinitionHolder都会注册到DefinitionListableBeanFactory中,也就是经过这个步骤,DefaultListableBeanFactory的变量beanFactory已经包含了所有解析好的配置。