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并加载配置文件。