Spring配置文件解析(一)ClassPathXmlApplicationContext源码分析

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这个类的分析,我们接下来会继续,欢迎交流!

    原文作者:Spring Cloud
    原文地址: https://blog.csdn.net/ONROAD0612/article/details/85125178
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞