Spring源码(二)— AbstractApplicationContext

news2024/11/26 16:26:56

上一篇文章简单的介绍了一下Spring框架大体的一个执行流程,整个专栏的内容也会根据第一篇序言中的流程图一步一步的向下梳理,并会慢慢补充更多的细节进去。

Test

创建ClassPathXmlApplicationContext来解析xml。

public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-${username}.xml");
    }
}

创建BeanFactory

按照序言中的流程图来讲,应该是先读取并解析xml配置文件,但xml的读取完的内容放哪呢? 应该是放在BeanFactory中,所以在解析xml之前应该先创建BeanFactory以及一些初始化的操作。

ClassPathXmlApplicationContext

调用ClassPathXmlApplicationContext构造方法

public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		// 调用父类构造方法,进行相关的对象创建等操作,包含属性的赋值操作
		super(parent);
		//可能会解析多个配置文件,configLocations是配置文件的字符串数组,设置配置文件路径
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

super(parent)

调用父类构造方法,相关对象进行创建,属性赋值。

//其最上级的父类为AbstractApplicationContext,调用父类的无参构造方法
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
		this();
		setParent(parent);
	}

this()

调用父类构造方法,创建PathMatchingResourcePatternResolver来解析XML配置文件

//父类构造方法,创建 资源模式处理器(主要处理XML)
public AbstractApplicationContext() {
		// 创建资源模式处理器
		this.resourcePatternResolver = getResourcePatternResolver();
	}

protected ResourcePatternResolver getResourcePatternResolver() {
		// 创建一个资源模式解析器(其实就是用来解析xml配置文件)
		return new PathMatchingResourcePatternResolver(this);
	}

setParent(parent)

如果ApplicationContext (第一次进来为null)不为null, 则获取Environment对象,如果Env对象属于ConfigurableEnvironment,则进行merge。

public void setParent(@Nullable ApplicationContext parent) {
		this.parent = parent;
		if (parent != null) {
			Environment parentEnvironment = parent.getEnvironment();
			if (parentEnvironment instanceof ConfigurableEnvironment) {
				getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
			}
		}
	}

getEnvironment
默认创建一个StandardEnvironment对象,SE对象会调用父类AbstractEnvironment构造方法,父类构造方法中,会调用customizePropertySources方法来为属性赋值,由SE实现该方法

public ConfigurableEnvironment getEnvironment() {
		if (this.environment == null) {
			this.environment = createEnvironment();
		}
		return this.environment;
	}
	
protected ConfigurableEnvironment createEnvironment() {
		return new StandardEnvironment();
	}

public AbstractEnvironment() {
		customizePropertySources(this.propertySources);
	}

//getSystemProperties()和getSystemEnvironment()两个方法会获取系统属性和环境
protected void customizePropertySources(MutablePropertySources propertySources) {
		propertySources.addLast(
				new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		propertySources.addLast(
				new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}

setConfigLocations(configLocations)

设置配置文件路径,因为Test测试类中只传了一个配置文件,所以此时locations = spring-${username}.xml

public void setConfigLocations(@Nullable 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
因为第一次ApplicationContext为null,所以不会getEnvironment方法,StandardEnvironment对象会在此时进行创建。

protected String resolvePath(String path) {
		
		return getEnvironment().resolveRequiredPlaceholders(path);
	}
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
		if (this.strictHelper == null) {
			this.strictHelper = createPlaceholderHelper(false);
		}
		return doResolvePlaceholders(text, this.strictHelper);
	}

//创建Helper对象,帮助替换xml文件中的值
//placeholderPrefix:前缀 ${
//placeholderSuffix:后缀 }
//valueSeparator :  值分隔符 :
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
		return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
				this.valueSeparator, ignoreUnresolvablePlaceholders);
	}

doResolvePlaceholders
替换配置文件中的占位符

private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
		return helper.replacePlaceholders(text, this::getPropertyAsRawString);
	}

parseStringValue
helper.replacePlaceholders方法会调用此方法进行值的替换,因为是spring-${username}.xml的写法。所以在替换时,会根据getEnvironment中获取的SystemProperties的系统属性username来动态的替换它,替换后,根据个人主机不同,我的主机username为3456,替换后配置文件名为spring-3456.xml。

protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
		//获取前缀下标
		int startIndex = value.indexOf(this.placeholderPrefix);
		//看是否包含
		if (startIndex == -1) {
			return value;
		}

		StringBuilder result = new StringBuilder(value);
		while (startIndex != -1) {
			//获取后缀 支持复杂如 spring-${abc${bcd}}形式的写法
			int endIndex = findPlaceholderEndIndex(result, startIndex);
			if (endIndex != -1) {
			//如果是复杂方式写法,则递归调用该方法进行解析
				String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
				String originalPlaceholder = placeholder;
				if (visitedPlaceholders == null) {
					visitedPlaceholders = new HashSet<>(4);
				}
				if (!visitedPlaceholders.add(originalPlaceholder)) {
					throw new IllegalArgumentException(
							"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
				}
				// Recursive invocation, parsing placeholders contained in the placeholder key.
				placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
				// Now obtain the value for the fully resolved key...
				String propVal = placeholderResolver.resolvePlaceholder(placeholder);
				if (propVal == null && this.valueSeparator != null) {
					int separatorIndex = placeholder.indexOf(this.valueSeparator);
					if (separatorIndex != -1) {
						String actualPlaceholder = placeholder.substring(0, separatorIndex);
						String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
						propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
						if (propVal == null) {
							propVal = defaultValue;
						}
					}
				}
				if (propVal != null) {
					// Recursive invocation, parsing placeholders contained in the
					// previously resolved placeholder value.
					propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
					result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
					if (logger.isTraceEnabled()) {
						logger.trace("Resolved placeholder '" + placeholder + "'");
					}
					startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
				}
				else if (this.ignoreUnresolvablePlaceholders) {
					// Proceed with unprocessed value.
					startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
				}
				else {
					throw new IllegalArgumentException("Could not resolve placeholder '" +
							placeholder + "'" + " in value \"" + value + "\"");
				}
				visitedPlaceholders.remove(originalPlaceholder);
			}
			else {
				startIndex = -1;
			}
		}
		return result.toString();
	}

在这里插入图片描述
return返回后,configLocations;变量此时已经变成了具体的文件名。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/608290.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

剖析 OpenShift 中的 DNS

深入分析 OpenShift 内部 DNS OpenShift 中的DNS 相关组件及其配置1.1 Pod 中的 DNS 配置1.2 Pod 所在宿主机上的 DNS 配置及服务1.2.1 resolv.conf 文件 DNS 配置DNS 查询流程为什么需要内部 DNS&#xff1f; 本文基于 OpenShift 3.11&#xff0c;Kubernetes 1.11 进行测试 O…

2023/6/4周报

目录 摘要 论文阅读 1、标题和现存问题 2、使用GNN进行文本分类 3、INDUCT-GCN 4、实验准备 5、实验结果 深度学习 1、时空图的种类 2、图在环境中的应用 3、STGNN 总结 摘要 本周在论文阅读上&#xff0c;阅读了一篇InducT-GCN:归纳图卷积文本分类网络的论文。基…

python-pandas按各种时间统计和案例

使用到的库 pandas、matplotlib、numpy 使用到的函数 df.resample(“H”).sum() 参数 B business day frequency C custom business day frequency (experimental) D calendar day frequency W weekly frequency M month end frequency BM business month end frequency CBM…

【奶奶看了都会】云服务器ChatGLM模型fine-tuning微调,让你拥有自己的知识库

1.背景 大家好啊&#xff0c;上次给大家写了ChatGLM-6B的部署使用教程&#xff0c;【奶奶看了都会】云服务器部署开源ChatGLM-6B&#xff0c;让你拥有自己的ChatGPT 但是因为模型比较小的问题&#xff0c;所以日常工作中可能用不上。而且大家更希望的是模型能训练自己的数据&…

【Python Bokeh】零基础也能轻松掌握的学习路线与参考资料

Python Bokeh是一款为开发者提供数据可视化的Python库。它可以帮助开发者轻松地创建交互式网页应用程序&#xff0c;而无需编写大量的JavaScript代码。Bokeh支持各种绘图类型和工具&#xff0c;包括线图、散点图、条形图等。Python Bokeh非常适合在大数据分析、商业智能和数据科…

chatgpt赋能python:Python去除重复元素的几种方法

Python去除重复元素的几种方法 在Python编程中&#xff0c;去除列表、集合、字典等数据结构中的重复元素是一个常见的操作。本文将介绍Python中去除重复元素的几种方法&#xff0c;并分析它们的优缺点。 方法一&#xff1a;使用set去重 Set是Python中的一种集合类数据结构&a…

17_Linux根文件简介与Busybox构建文件系统

目录 根文件系统简介 文件目录简介 BusyBox简介 编译BusyBox构建根文件系统 修改Makefile添加编译器 busybox中文字符支持 配置 busybox 编译busybox 向根文件系统添加lib库 向rootfs的“usr/lib”目录添加库文件 创建其他文件夹 根文件系统初步测试 根文件系统简介…

Unity制作二次元卡通渲染角色材质——3、高光反射与ILM贴图

Unity制作二次元材质角色 回到目录 大家好&#xff0c;我是阿赵。 这里继续来讲二次元角色的材质。上次讲了光影的色阶化问题&#xff0c;这次继续讲光照模型效果的问题。 之前我们说过&#xff0c;光照模型的最后效果是&#xff1a; 环境色漫反射高光反射。 这里我们可以先忽略…

【嵌入式系统】思考题复习

嵌入式系统思考题 0. 名词解释1. 嵌入式系统概述2. ARM处理器体系结构3. ARM指令集4. S5PV210微处理器与接口5. ARM-Linux内核6. 嵌入式Linux文件系统7. 嵌入式Linux系统移植及调试8. 设备驱动程序设计9. QT图形界面应用程序开发基础10. SQLite数据库11. 嵌入式系统的开发设计案…

Qt6.5.1LTS搭建Android开发环境填坑

Qt6第二个LTS出来了&#xff0c;周日找时间安装并测试了Qt6.5LTS&#xff0c;安装我是按我之前的一个博客记录来做的&#xff0c;用的是国内境像&#xff0c;顺利快速安装完成&#xff0c;下面是设置的过程关键总结。 一、Devices Android设备(Device)选择 二、Kits &#xf…

网络安全-XSS的原理、攻击及防御

简介 跨站脚本攻击(全称Cross Site Scripting,为和CSS&#xff08;层叠样式表&#xff09;区分&#xff0c;简称为XSS)是指恶意攻击者在Web页面中插入恶意javascript代码&#xff08;也可能包含html代码&#xff09;&#xff0c;当用户浏览网页之时&#xff0c;嵌入其中Web里面…

【C++开发】Qt+Tesseract实现文字识别的各种坑(已解决)

文章目录 Tesseract库下载Qt版本选择安装步骤 VS2017安装MSVC调试器安装构建套件的导入文字识别功能的检测 最近在给之前Qt医疗管理系统项目添加一个文字识别功能&#xff0c;但是在其中遇到非常多坑&#xff0c;花费了我比较多的时间&#xff08;查阅了很多文章&#xff09;&a…

基础学习——关于list、numpy、torch在float和int等数据类型转换方面的总结

系列文章目录 Numpy学习——创建数组及常规操作&#xff08;数组创建、切片、维度变换、索引、筛选、判断、广播&#xff09; Tensor学习——创建张量及常规操作&#xff08;创建、切片、索引、转换、维度变换、拼接&#xff09; 基础学习——numpy与tensor张量的转换 基础学习…

【软件测试】一个完整的项目流程是什么样的?

软件测试是使用人工或者自动的手段来运行或者测定某个软件系统的过程&#xff0c;其目的在于检验它是否满足规定的需求或弄清预期结果与实际结果之间的差别。 在软件投入使用前&#xff0c;要经过一系列的严格测试&#xff0c;才能保证交付质量。 一、引言 1.编写目的 本文档…

2023年度第四届全国大学生算法设计与编程挑战赛(春季赛)

目录 2023年度第四届全国大学生算法设计与编程挑战赛&#xff08;春季赛&#xff09;1、A2、Bx3、Cut4、Diff5、EchoN6、Farmer7、GcdGame8、HouseSub9、IMissYou!10、Jargonless 2023年度第四届全国大学生算法设计与编程挑战赛&#xff08;春季赛&#xff09; 1、A 题目描述…

【C++初阶】C/C++内存管理(没有对象的都进来看看吧~)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

2023 华为 Datacom-HCIE 真题题库 08/12--含解析

单项选择 1.[试题编号&#xff1a;190385] &#xff08;单选题&#xff09;以下关于BGP/MPLSIPVPN路由交互的描述&#xff0c;错误的是哪一项? A、PE与CE之间交互的是IPv4路由信息 B、出口PE可以通过BGP、IGP或静态路由的方式向远端CE发送IPv4路由 C、入口PE将从CE接收到的I…

ODOO随笔(二)—— Odoo16的主题样式变更

1 登陆界面的修改 系统默认的登陆界面&#xff0c;有更改odoo logo和去除“管理数据库”、“由Odoo提供支持”两个需求。 &#xff08;1&#xff09;更改odoo logo 系统管理员登陆后&#xff0c;选择菜单栏&#xff1a;设置——公司——管理公司 点击相机图标&#xff0c;上传…

Round#13 web专项部分wp

flask?jwt? 忘记密码处有secretkey 然后就是伪造session了,这里猜一下要什么 最后应该是_is_admin有用,我没细看,当时平台卡麻了 指正:改_user_id为1 ez_factors 注意到可以拼接命令,但是执行的回显只有数字 那么可以考虑读取后用od命令来转8进制读取 flask?jwt?(hard) …

2023.5.30 深信服 c++ 一面

深信服&#xff0c;c一面小记 导语面试内容重点问题解析弱引用弱在哪里&#xff1f;手撕memcpy水壶倒水问题 导语 最近开始面试&#xff0c;记录一下面试经历。   应该是会给我发感谢信的吧~我也是真的菜。工作的原因其实也没时间准备&#xff0c;最近工作还是挺忙的。另外一…