之前的分析都是在XmlBeanFactory的基础上进行的分析,现在我们开始分析ApplicationContext,相比之下ApplicationContext提供了更多的扩展功能.
首先看看使用两个不同的类去加载配置文件在写法上的不同:
- 使用BeanFactory
BeanFactory bf = new XmlBeanFactory(new ClassPathResource(“beanFactoryTest.xml));
- 使用ApplicationContext
ApplicationContext bf = new ClassPathXmlApplicationContext("beanFactoryTest.xml");
同样以ClassPathXmlApplicationContext作为切入点,开始对整体功能进行分析
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
// 设置配置路径
this.setConfigLocations(configLocations);
if (refresh) {
// 功能扩展
this.refresh();
}
}
ClassPathXmlApplicationContext可以将配置文件路径以数组的方式传入,ClassPathXmlApplicationContext可以对数组进行解析并进行加载.对于解析及功能实现都在refresh()中实现.
1、设置配置路径
public void setConfigLocations(@Nullable 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;
}
}
主要用于解析给定的路径数组,当然,如果数组中包含特殊符号,如${var},那么在resolvePath中会搜寻匹配的系统变量并替换.
2、功能扩展
设置好路径之后,就可以根据路径做配置文件的解析及各种功能的实现类.
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1、准备刷新上下文环境
prepareRefresh();
// 2、初始化beanFactory,并进行XML文件读取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3、对beanFactory进行各种功能的填充
prepareBeanFactory(beanFactory);
try {
// 4、子类覆盖方法做额外的处理
postProcessBeanFactory(beanFactory);
// 5、激活各种BeanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 6、注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用在getBean的时候
registerBeanPostProcessors(beanFactory);
// 7、为上下文初始化Message源,即不同语言的消息体,国际化处理
initMessageSource();
// 8、初始化应用消息广播器,并放入"applicationEventMulticaster"bean中
initApplicationEventMulticaster();
// 9、给子类初始化其他的bean
onRefresh();
// 10、在所有注册的bean中查找Listener bean,注册到消息广播器中
registerListeners();
// 11、初始化剩下的单实例(非惰性的)
finishBeanFactoryInitialization(beanFactory);
// 12、完成刷新过程,通知生命周期处理器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;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
概括一下上面每一步都做了什么:
- 初始化前的准备工作,例如对系统属性或者环境变量进行准备及验证
- 初始化BeanFactory,并进行XML文件读取
ClassPathXmlApplicationContext包含着BeanFactory所提供的一切特征,这里会复用BeanFactory中的配置文件读取解析及其他功能,这一步之后ClassPathXmlApplicationContext实际上就已经包含了BeanFactory所提供的功能,也就是可以进行beanDefinition的提取等基础操作了. - 对BeanFactory进行各种功能填充.
- 子类覆盖方法做额外的处理.
- 激活各种BeanFactoryPostProcessor
- 注册拦截bean创建的beanPostProcessor,这里只是注册,真正的调用是在getBean时候.
- 为上下文初始化Message源,即对不同语言的消息体进行国际化处理
- 初始化应用消息广播器,并放入applicationEventMulticaster的bean中
- 留给子类来初始化其他的bean
- 在所有注册的bean中查找listener bean,注册到消息广播器中
- 初始化剩下的单实例(非惰性的)
- 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人.
下面我们先简单分析一下前两步
环境准备
protected void prepareRefresh() {
// Switch to active.
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// 留给子类覆盖
initPropertySources();
// 验证需要的属性文件是够都已经放入环境中
getEnvironment().validateRequiredProperties();
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
this.earlyApplicationEvents = new LinkedHashSet<>();
}
initPropertySources
:符合spring中的开发式结构设计,给用户最大的扩展Spring的能力.用户可以根据自身的需要重写该方法,并在方法中进行个性化的属性处理及设置.
validateRequiredProperties
:对属性进行验证
就上面两个方法,举个小例子:
比如有一个需求,工程在运行过程中用到的某个设置(例如Var)是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,工程就不能工作.
我们可以自定义类:
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
public MyClassPathXmlApplicationContext(String... configLocations) throws BeansException {
super(configLocations);
}
@Override
protected void initPropertySources() {
getEnvironment().setRequiredProperties("Var");
}
}
上面我们重写了initPropertySources,方法,并且设置了环境变量中需要验证的变量,那么在验证的时候也就是程序到getEnvironment().validateRequiredProperties()的时候,如果系统的环境并有检测到对Var的环境变量,那么将抛出异常.
测试:
ApplicationContext bf = new MyClassPathXmlApplicationContext("beanFactoryTest.xml");
Cat cat = (Cat) bf.getBean("blackCat");
加载BeanFactory
obtainFreshBeanFactory()方法就是用来获取beanFactory的,经过这个方法之后,ApplicationContext就拥有了BeanFactory的全部功能了.
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 1、初始化BeanFactory,并进行XML文件的读取解析,并将得到的BeanFactory记录在当前实体的属性中
refreshBeanFactory();
// 2、返回当前实体的beanFacotry属性
return getBeanFactory();
}
refreshBeanFactory
protected final void refreshBeanFactory() throws BeansException {
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
// 创建DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
// 指定序列化Id
beanFactory.setSerializationId(this.getId());
// 定制beanFactory
this.customizeBeanFactory(beanFactory);
// 记载beanDefinition
this.loadBeanDefinitions(beanFactory);
synchronized(this.beanFactoryMonitor) {
// 使用全局变量记录BeanFactory类实例
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
}
}
- 定制beanFactory
在基本容器的基础上,添加了是否允许覆盖、是否允许扩展
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
// 如果属性allowBeanDefinitionOverriding不为空,设置给beanFactory对象相应属性
// 该属性的作用是:是否允许覆盖相同名称的不同定义的对象
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 如果属性allowCircularReferences不为空,设置给beanFactory对象相应属性
// 此属性的作用:是否允许bean之间存在循环依赖
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
对于允许覆盖和允许依赖的设置这里只是判断了是否为空,如果不为空要进行设置,但是并没有看到在哪里进行了设置.其实可以用子类覆盖的方式:
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
.....
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
super.setAllowBeanDefinitionOverriding(true);
super.setAllowCircularReferences(true);
}
}
- 加载BeanDefinition
在loadBeanDefinitions方法中首先初始化:XmlBeanDefinitionReader,用它来读取XML文件
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 为beanFactory创建XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 对beanDefinitionReader进行环境变量的设置
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 对beanDefinitionReader进行设置,可以覆盖
initBeanDefinitionReader(beanDefinitionReader);
// 读取加载配置文件
loadBeanDefinitions(beanDefinitionReader);
}
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的loadBeanDefinitions方法进行配置文件的加载注册,就是之前讲BeanFactory的时候的套路,因为XmlBeanDefinitionReader已经讲之前初始化的beanFactory注册进去了,所以XmlBeanDefinitionReader所读取的BeanDefinitionHolder都会注册到beanFactory中,也就是这步之后,beanFactory就包含了所有解析好的配置.