spring源码系列03-ClasspathXmlApplicationContext的启动过程分析1
从这一节开始我们来分析下ClasspathXmlApplicationContext这个容器的启动过程。首先祭出使用该容器的一段代码
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = app.getBean(Person.class);
System.out.println(person);
}
还有该容器的继承体系类图
一、从构造方法开始
上边这段代码,是从创建一个ClassPathXmlApplicationContext
对象开始的,所以我们也从这个类的构造方法开始。
点进去这个接收配置文件路径参数的构造方法
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
发现它调用了另一个构造方法,继续深入
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);//设置父容器
setConfigLocations(configLocations);//处理配置文件参数
if (refresh) {
refresh();//重要
}
}
第一句是给当前容器设置父容器,当前传过来的是null,先不关注。
第二句是对配置文件参数进行处理,并赋值给成员变量configLocations
,后边会使用到,根据这个路径进行解析。
最后这个refresh()方法比较重要,就是在这个方法中完成了对bean的创建,这也是为什么applicationContext类型的容器启动后bean就已经创建好的原因。
二、refresh方法
首先,点进去这个方法,发现它是定义在AbstractApplicationContext
类中的,从上边的类图中可以看出继承关系
先简单贴下这个方法的代码,只贴出这篇分析到的内容,剩余的我们后边再看。
Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
//一些刷新前的准备工作
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//获取容器内的DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//一些准备工作
prepareBeanFactory(beanFactory);
try {
//xian 省略
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
看源码的时候一定要注意把握大方向,先整体理解,然后再慢慢扣细节,刚开始细节看不动不要紧,不要每个方法,每一句都去看。
上边的代码,prepareRefresh这句从源码注释和方法名称看就是一些准备工作,点进去发现记录了开始时间和结束时间等,不影响大局,不重要所以先跳过不看。
接着这个obtainFreshBeanFactory()
就比较重要了,它是用来获取ApplicationContext容器内部的BeanFactory的,我们以前的文章里有分析过ApplicationContext容器的内部会持有一个BeanFactory容器,实际持有的是
DefaultListableBeanFactory,所有的bean都是放在这个BeanFactory里的。
三、obtainFreshBeanFactory()方法分析
上边讲到这个方法是用来创建容器内部beanFaxctory的,直接上源码
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
下面一句是返回beanFactory,所以创建是在上边的refreshBeanFactory()中。这个方法是AbstractApplicationContext
中的抽象方法,当我们使用ClasspathXmlApplicationContext时,对应的实现类是
AbstractRefreshableApplicationContext
,从上边的类图可以看出继承关系。看下这个类中此方法的源码
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory:
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建beanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//加载xml配置文件
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
关注下try中的代码,第一句就创建了一个DefaultListableBeanFactory类型的beanFactroy,这也是对前面几篇中内容的一个验证。在点进去这个createBeanFactory()可以看到确实是创建了一个这个类型的容器。
接着看loadBeanDefinitions(beanFactory);这里就是在加载配置文件,把配置文件中的bean定义信息加载到ApplicationContext内部的beanFactory中。loadBeanDefinitions是类AbstractRefreshableApplicationContext
,中的抽象方法,实现类是AbstractXmlApplicationContext
点进去看下这个方法的源码
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions:
@Override
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);
}
可以看到上边的代码先创建了一个XmlBeanDefinitionReader
,然后通过它来加载配置文件,和在上一篇文章中我使用的方式是一样的。点进去这个loadBeanDefinitions(beanDefinitionReader);会发现确实是调用了
reader.loadBeanDefinitions
经过上边的这些步骤,obtainFreshBeanFactory方法就走完了,此时ApplicationContext容器内部的DefaultListableBeanFactory已经创建好了,并且也已经加载了配置文件中的bean定义信息。
四、总结
通过上边的分析,可以了解到ClassPathXmlApplicationContext
的构造方法中会调用父类AbstractApplicationContext
中的refresh方法,在这个方法中会做许多事情,这一节我分析了第一步,创建容器内部的DefaultListableBeanFactory并加载配置文件。