Spring源码系列 —— 构造和初始化上下文

栏目: Java · 发布时间: 5年前

内容简介:探索spring源码实现,精华的设计模式,各种jdk提供的陌生api,还有那么点黑科技都是一直以来想做的一件事!但是读源码是一件非常痛苦的事情,需要有很大的耐心和扎实的基础。spring上下文的实现非常多,其中基于Xml启动的有ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等等。这些上下文都非常类似,基于解析Xml,获取配置的bean,最终完成上下文的启动加载过程。 这里以ClassPathXmlApplicationConte

探索spring源码实现,精华的设计模式,各种jdk提供的陌生api,还有那么点黑科技都是一直以来想做的一件事!但是读源码是一件非常痛苦的事情,需要有很大的耐心和扎实的基础。

在曾经读两次失败的基础上,这次希望能一站到底!这个系列基于spring v4.3.20版本探索。

Spring上下文启动加载过程的分段

spring上下文的实现非常多,其中基于Xml启动的有ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等等。这些上下文都非常类似,基于解析Xml,获取配置的bean,最终完成上下文的启动加载过程。 这里以ClassPathXmlApplicationContext为主分析Spring ApplicatonContext整个启动过程。

这里将spring上下文的启动分为两个大步骤:

  • 上下文对象本身的构造和初始化:创建ClassPathXmlApplicationContext对象,构造器中执行一些初始化操作,如:设置上下文的环境Enviroment、设置上下文的资源解析器等
  • 上下文获取bean配置,解析实例化bean:构造BeanFactory,解析Xml,构造bean定义,依赖注入,实例化bean。这个过程非常复杂;

Spring源码系列 —— 构造和初始化上下文

这节主要分析第一个阶段:上下文对象本身的构造和初始化。

上下文对象本身的构造和初始化

编写debug代码,构造ClassPathXmlApplicationContext对象:

ApplicationContext context = new ClassPathXmlApplicationContext("/beans.xml");
HelloWorldBean h = context.getBean("helloWorld", HelloWorldBean.class);
h.printHelloWorld();
复制代码

编写配置文件beans.xml,配置helloWorld的bean:

<bean id="helloWorld" class="com.learn.ioc.beans.HelloWorldBean"></bean>
复制代码

下面主要分析ClassPathXmlApplicationContext new的过程。ClassPathXmlApplicationContext构造函数如下:

// 使用beans.xml作为上下文的配置文件构造ApplicationContext对象
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
	this(new String[] {configLocation}, true, null);
}

// 以配置文件、是否刷新上下文、父上下文作为参数构造ApplicationContext对象
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
		throws BeansException {
	super(parent);
	setConfigLocations(configLocations);
	if (refresh) {
		refresh();
	}
}
复制代码

super(parent)和setConfigLocations(configLocations)方法对应两阶段中的第一阶段: 上下文对象本身的构造和初始化 。refresh()方法完成第二阶段: 上下文获取bean配置,解析实例化bean

super(parent)以该上下文的父上下文作为参数调用父类的构造器,这里由于没有父上下文,所以为 null

public AbstractXmlApplicationContext(ApplicationContext parent) {
	super(parent);
}
复制代码

AbstractXmlApplicationContext是以Xml作为配置的Spring上下文,AbstractXmlApplicationContext又继续调用其父类的构造器:

public AbstractRefreshableConfigApplicationContext(ApplicationContext parent) {
	super(parent);
}
复制代码

AbstractRefreshableConfigApplicationContext也是以Xml文件作为基础配置的上下文,但是它具有可以刷新配置文件的能力,AbstractRefreshableConfigApplicationContext中提供了setConfigLocations方法可以用于设置配置文件。是Xml配置文件上下文的基石。 该上下文中又调用父类构造函数:

public AbstractRefreshableApplicationContext(ApplicationContext parent) {
	super(parent);
}
复制代码

Spring上下文可以称作为上下文家族,继承有多代。其中AbstractRefreshableApplicationContext是上下文中家族中的元老。它提供了两个非常重要的接口refreshBeanFactory()和loadBeanDefinitions(DefaultListableBeanFactory beanFactory)用于配置BeanFactory和定义接下载入Bean的接口。虽然构造函数中依然调用父类的构造函数,但是它的确非常重要:

public AbstractApplicationContext(ApplicationContext parent) {
	this();
	// 设置该上下文的父上下文
	setParent(parent);
}

public AbstractApplicationContext() {
	// 初始化资源解析器,用于解析获取Xml配置
	this.resourcePatternResolver = getResourcePatternResolver();
}
复制代码

上下文家族的鼻祖AbstractApplicationContext中定义了整个Bean的声明周期和处理过程。在构造器中初始化resourcePatternResolver资源解析器,赋予ApplicationContext具有解析资源的能力。且设置父上下文。

getResourcePatternResolver主要用来创建资源解析器:

protected ResourcePatternResolver getResourcePatternResolver() {
	return new PathMatchingResourcePatternResolver(this);
}
复制代码

PathMatchingResourcePatternResolver主要以路径匹配的模式进行解析获取资源,其主要能力和实现后续会详细介绍。

setParent(parent)主要用于设置该上下文的父上下文:

public void setParent(ApplicationContext parent) {
	// 设置父上下文
	this.parent = parent;
	// 如果父上下文不空
	if (parent != null) {
	  	// 获取父上下文的环境变量Environment
		Environment parentEnvironment = parent.getEnvironment();
		if (parentEnvironment instanceof ConfigurableEnvironment) {
			// 将父上下文的环境变量Environment合并至该级上下文环境变量中
			getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
		}
	}
}
复制代码

经过一系列的父类的构造器的调用,Spring上下文完成了多级上下文的载入过程。可以从下图看出其继承顺序关系:

Spring源码系列 —— 构造和初始化上下文

Tips

上下文的实现中 设计模式 非常强,秉着设计的六大原则进行实现:

单一职责原则:每种上下文都有自己的职责能力,使得上下文的扩展能力极强; 

开闭原则:AbstractRefreshableApplicationContext提供了对loadBeanDefinitions的定义由其子类按照不同加载Bean的逻辑各自实现; 

依赖倒置原则:通过多级的抽象,提供了不同的接口,达到针对接口编程;  

ClassPathXmlApplicationContext中调用层级父类上下文对象的构造后,再执行AbstractRefreshableConfigApplicationContext中实现的setConfigLocations设置上下文的Xml配置:

// 设置配置文件路径
public void setConfigLocations(String... locations) {
	// 如果不为空,则构早配置文件路径,支持多个配置文件
	if (locations != null) {
		Assert.noNullElements(locations, "Config locations must not be null");
		this.configLocations = new String[locations.length];
		// 循环解析多个配置文件路径
		for (int i = 0; i < locations.length; i++) {
			this.configLocations[i] = resolvePath(locations[i]).trim();
		}
	}
	else {
		this.configLocations = null;
	}
}
复制代码

关于resolvePath的具体过程涉及到Spring IOC容器的环境Enviroment组件,具体的解析路径的过程将在下章的探索Enviroment中详解。

总结

Spring上下文类型非常繁多,其中有直接面向各种场景直接使用的FileSystemApplicationContext、ClasspathXmlApplicationContext等等,还有很多实现基础能力的上下文: AbstractApplicationContext是上下文实现中的基石,其中定义了上下文Bean对象依赖注入的模板; AbstractRefreshableApplicationContext也是非常重要的上下文,其中组合了BeanFactory和定义加载Bean的接口;


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

比特币

比特币

李钧、长铗 / 中信出版社 / 2014-1-1 / 39.00元

2009年,比特币诞生。比特币是一种通过密码编码,在复杂算法的大量计算下产生的电子货币。虽然是虚拟货币,比特币却引起了前所未有的全球关注热潮。 这一串凝结着加密算法与运算能力的数字不仅可以安全流通、换取实物,1比特币价值甚至曾高达8 000元人民币。有研究者认为比特币具备打破几千年来全球货币由国家垄断发行的可能性。在不经意间,比特币引起的金融新浪潮已悄然成型。 虚拟货币并不是新鲜事物,......一起来看看 《比特币》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试