1 概述
针对前面的SpringIOC的源码分析,我们看见了在整个Spring容器初始化的时候,配置文件的解析是相当重要的,所以从这篇文章开始,我们将对配置文件的解析进行深入的分析。
这里我们仅仅拿ClassPathXmlApplicationContext类来进行分析,因为FileSystemXmlApplicationContext的配置文件解析的过程和ClassPathXmlApplicationContext是相同的。
2 详细分析
在我们的测试当中使用到了如下的代码。
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring_test.xml");
Animal animal = (Animal) ctx.getBean("animal");
animal.eat();
这里调用了ClassPathXmlApplicationContext类的构造函数。
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
//这里支持自动刷新Spring容器
this(new String[] {configLocation}, true, null);
}
而在这个构造函数中又调用了下面这个构造函数。
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
//设置配置文件地址
setConfigLocations(configLocations);
if (refresh) {
//刷新容器,至此,我们可以猜想配置文件的加载其实是在这个函数里面实现的。
refresh();
}
}
那么refresh()函数中又干了什么呢?针对refresh函数我们前面已经分析过,其实这里只需要关注下面这行代码就行了,
public void refresh() throws BeansException, IllegalStateException {
... ...
//告述子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
... ...
}
刷新bean工厂后返回,然后再对bean工厂进行其余的处理。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//这是一个抽象函数,具体实现留给子类
refreshBeanFactory();
//这里的getBeanFactory也是一个抽象函数
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
上面的代码,我们可以猜想getBeanFactory函数返回的就是refreshBeanFactory刷新的Bean工厂。下面我们继续来看refreshBeanFactory函数的源码。
protected final void refreshBeanFactory() throws BeansException {
//有bean工厂
if (hasBeanFactory()) {
//这里其实就是删除了所有单列bean(其实就是置空了和bean相关的缓存)。
destroyBeans();
//这里其实就是将bean工厂设置成了null
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//真正的加载配置文件是在这一步完成的
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
接下来继续看loadBeanDefinitions(beanFactory);这一步的源码。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
上面的函数其实是生成XmlBeanDefinitionReader对象后调用了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。
简单来说在ApplicationContext中所做的操作是初始化了一个BeanFactory和XmlBeanDefinitionReader,针对XmlBeanDefinitionReader这个类的分析,我们接下来会继续,欢迎交流!