Spring源码分析:Spring IOC容器初始化

5,032 阅读7分钟

概述

Spring 对于Java 开发来说,以及算得上非常基础并且核心的框架了,在有一定开发经验后,阅读源码能更好的提高我们的编码能力并且让我们对其更加理解。俗话说知己知彼,百战不殆。当你对Spring 掌握熟透之后,也就没什么能过阻拦你在开发路上前进了。

IOC 总体来说有两处地方最重要,一个是创建Bean容器,一个是初始化。在本文中,主要为大家讲解了 IOC Bean 容器创建过程。后续将会补上初始化部分的知识。

为了保持文章的严谨性,如果读者发现我哪里说错了请一定不吝指出,非常希望可以听到读者的声音。同时能过纠正自己的误解。

本文主要采用了ClassPathXmlApplication作为Spring IOC 容器,从 ClassPathXmlApplication 分析容器创建的过程,首先来看一下ClassPathXmlApplication 的依赖关系:

首先我们先来观察一下核心主干:

我们可以通过对ClassPathXmlApplicationContext 的父类名称,了解其主要功能:

  • DefaultResourceLoader:提供获取配置文件方法 getResource(),返回资源信息 Resource
  • AbstractApplicationContext:提供主要创建容器,初始化对象方法 refresh()
  • AbstractRefreshableApplicationContext: 提供刷新 refreshBeanFactory() 方法,进行BeanFactory 的初始化。
  • AbstractRefreshableConfigApplicationContext:保存配置文件信息。
  • AbstractXmlApplicationContext:提供 loadBeanDefinitions() 读取BeanDefinitions方法,将资源转换为配置。
  • ClassPathXmlApplicationContext:具体实现类,用于定位资源文件。

通过上述主要类的分析,相信大家对Spring IOC 的初始化有了大概的认识。

简单来说,Spring IOC 容器创建的具体流程如下:

我们将图片对应到类的调用,具体步骤为:

1、ClassPathXmlApplicationContext -> 通过构造器,读取XML配置文件地址信息

2、AbstractApplicationContext -> refresh() 初始化容器

3、AbstractRefreshableApplicationContext -> BeanFactory 初始化

4、AbstractXmlApplicationContext -> loadBeanDifinition 读取资源信息(加载Bean)

5、XmlBeanDefinitionReader -> 将资源文件解析成 BeanDefinition

6、DefaultBeanDefinitionDocumentReader -> 将BeanDefinition 注册到BeanFactory 中

由于Spring IOC部分源码主要包括了:容器创建以及Bean 初始化两部分。在本文内容中,主要介绍一下,容器创建的过程!

1、容器实例

ApplicationContext.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="User" class="com.charles.business.model.User">
        <property name="username" value="jaycekon"/>
        <property name="phone" value="1881412***"/>
    </bean>
</beans>

代码启动入口:

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        User user = (User) applicationContext.getBean("User");
        Assert.notNull(user, "容器初始化异常!");
        logger.info("初始化结果:{}", JSONObject.toJSONString(user));
    }

接下来我们根据ClassPathXmlApplication 的依赖图进行逐一分析每一个过程。

2、ClassPathXmlApplicationContext

核心方法:

org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String[], boolean, org.springframework.context.ApplicationContext)

从上图可以看出,ClassPathXmlApplicationContext 类中并没有提供什么特别的方法,除构造器外,只有一个获取Resource 的方法(与之相似的 AbstractRefreshableConfigApplicationContext 中提供类获取资源定位的方法),主要用于保存资源信息。

ClassPathXmlApplicationContext 中的主要构造方法:

	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
	// 调用父类构造方法
		super(parent);
	// AbstractRefreshableConfigApplicationContext 具体实现,保存资源定位信息
		setConfigLocations(configLocations);
		if (refresh) {
       //  AbstractApplicationContext 具体实现,初始化容器与Bean
			refresh();
		}
	}

可以看到,ClassPathXmlApplicationContext 这个类相当简单,主要作用:

  • Resource[] configResources:将配置文件作为资源都存放到这个数组中
  • setConfigLocations():保存配置文件定位信息(AbstractRefreshableConfigApplicationContext)
  • refresh():启动容器初始化核心方法(AbstractApplicationContext)

3、AbstractApplicationContext

核心方法:

org.springframework.context.support.AbstractApplicationContext#refresh

AbstractApplicationContext 是Spring IOC 容器中核心类,对父类,接口提供了具体实现,其中的方法非常丰富,在这里我们主要介绍其中的核心方法 refresh()。 这里简单说下为什么是 refresh(),而不是 init() 这种名字的方法。因为 ApplicationContext 建立起来以后,其实我们是可以通过调用 refresh() 这个方法重建的,这样会将原来的 ApplicationContext 销毁,然后再重新执行一次初始化操作。

public void refresh() throws BeansException, IllegalStateException {
		//对容器初始化进行加锁操作,比免在创建的同时,重复操作。
		synchronized (this.startupShutdownMonitor) {
			// 设置容器初始化世界,表示容器现在isActive,初始化上下文环境中的任何占位符属性源
			prepareRefresh();

			// 核心步骤,主要功能是:让子类进行BeanFactory 初始化,并且将Bean信息 转换为BeanFinition,最后注册到容器中
			// 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
			prepareBeanFactory(beanFactory);

			try {
			    // 后续步骤将在下次进行分析,本文主要核心为上面几个步骤
			    ...      
			}
			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();
			}
		}
	}

从AbstractApplicationContext 中的refresh()我们可以粗略的领会到,Spring IOC 的核心流程,基本在这里完成的,从容器创建,资源解析,Bean创建等一系列步骤。都是由这个方法进行控制。

在本文中,我们主要关心的一个点就是:

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

通过上图,我们可以直观的了解到ApplicationContext 与BeanFactory 之间的关系。这里创建的 ConfigurableListableBeanFactory 实际包含了BeanFactory 三个分支的绝大部分功能。

1、ApplicationContext 继承了 ListableBeanFactory,这个 Listable 的意思就是,通过这个接口,我们可以获取多个 Bean,最顶层 BeanFactory 接口的方法都是获取单个 Bean 的。

2、ApplicationContext 继承了 HierarchicalBeanFactory,Hierarchical 单词本身已经能说明问题了,也就是说我们可以在应用中起多个 BeanFactory,然后可以将各个 BeanFactory 设置为父子关系。

3、AutowireCapableBeanFactory 这个名字中的 Autowire 大家都非常熟悉,它就是用来自动装配 Bean 用的,但是仔细看上图,ApplicationContext 并没有继承它,不过不用担心,不使用继承,不代表不可以使用组合,如果你看到 ApplicationContext 接口定义中的最后一个方法 getAutowireCapableBeanFactory() 就知道了。

4、ConfigurableListableBeanFactory 也是一个特殊的接口,看图,特殊之处在于它继承了第二层所有的三个接口,而 ApplicationContext 没有。这点之后会用到。

我们来看一下 obtainFreshBeanFactory() 方法的主要内容:

	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		//AbstractRefreshableApplicationContext 中实现,主要用于刷新BeanFactory,加载 Bean 定义、注册 Bean 等等
		refreshBeanFactory();
		//获取上述步骤中 创建好的 BeanFactory
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}

4、AbstractRefreshableApplicationContext

核心方法:

org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory

其实在阅读源码的过程中,读懂类的命名能过为我们提供很大的帮助,例如 AbstractRefreshableApplicationContext 这个类,可以看出她继承了AbstractApplicationContext 并且主要实现了其中的refresh 功能,当然,这里的refresh 并不是指 refresh() 方法。而是指的:refreshBeanFactory()

	@Override
	protected final void refreshBeanFactory() throws BeansException {
		//判断当前ApplicationContext是否已经有 BeanFactory ,如果有,销毁所有 Bean,关闭 BeanFactory
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
		    // 初始化一个 DefaultListableBeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			
			// 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用
			customizeBeanFactory(beanFactory);
			
			// 读取配置文件信息,加载 Bean 到 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);
		}
	}

可能大家之前会之前会认为ApplicationContext 与 BeanFactory 之间是继承关系,但是在实际应用中,但是它不应该被理解为 BeanFactory 的实现类,ApplicationContext 是持有 BeanFactory 的一个实例,并且以后所有的 BeanFactory相关的操作其实是给这个实例来处理的。

简单介绍一下 customizeBeanFactory 方法:

  • allowBeanDefinitionOverriding:设置Bean 是否可以被覆盖
  • allowCircularReferences:设置是否可以循环依赖
	protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
		if (this.allowBeanDefinitionOverriding != null) {
			beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.allowCircularReferences != null) {
			beanFactory.setAllowCircularReferences(this.allowCircularReferences);
		}
	}

5、AbstractXmlApplicationContext

核心方法:

org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)

经历了那么长的路程,才终于到了XML -> Resource -> BeanDefinition 这个步骤:

	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		//给这个 BeanFactory 实例化一个 XmlBeanDefinitionReader BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// 设置默认环境配置
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// 初始化 BeanDefinitionReader
		initBeanDefinitionReader(beanDefinitionReader);
		// 将配置文件读取,解析成BeanDefinition
		loadBeanDefinitions(beanDefinitionReader);
	}

这个方法比较简单,我们直接往下走,进入到loadBeanDefinitions()方法中:

	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);
		}
	}

可以看到,上面主要分成了两个步骤,一个是通过Resource 进行解析,一个是通过configLocations 进行解析(这个步骤,主要多了将资源定位符抓换为Resource 的过程)。

后续的Bean 解析,将会在下述方法进行解析:

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)

6、总结

由于篇幅原因,本文主要介绍了Spring IOC 容器创建的源码过程,关于Bean 解析,以及后续步骤的源码分析,将会在后续补上。最后我们来回顾一下主流程:

  • ClassPathXmlApplicationContext.ClassPathXmlApplicationContext() 初始化
  • AbstractApplicationContext.refresh() 核心方法
  • AbstractRefreshableApplicationContext.refreshBeanFactory() 初始化BeanFactory
  • AbstractXmlApplicationContext.loadBeanDefinitions() 解析 Resource
  • 未完待续。。。

参考资料:

  • https://docs.spring.io/spring/docs/5.0.5.BUILD-SNAPSHOT/spring-framework-reference/core.html#spring-core
  • http://www.importnew.com/27469.html
  • http://www.importnew.com/19243.html