【spring源码系列-03】xml配置文件启动spring时refresh的前置工作

news2024/11/27 22:35:53

Spring源码系列整体栏目


内容链接地址
【一】spring源码整体概述https://blog.csdn.net/zhenghuishengq/article/details/130940885
【二】通过refresh方法剖析IOC的整体流程https://blog.csdn.net/zhenghuishengq/article/details/131003428
【三】xml配置文件启动spring时refresh的前置工作https://blog.csdn.net/zhenghuishengq/article/details/131066637

spring底层源码整体概述

  • 一,xml配置文件启动spring时refresh的前置工作
    • 1,super(parent)
    • 2,setConfigLocations()
      • 2.1,获取系统属性和系统环境
      • 2.2,解析系统环境和系统属性
    • 3,总结

一,xml配置文件启动spring时refresh的前置工作

前两篇大概的描述了一下springIoc的整体流程,接下来再对里面的细节进行分析。如下依旧是通过经典的xml的方式获取到上下文,并且在resources目录下配置一个spring.xml文件,这里推荐使用debug的方式,从上往下看

ApplicationContext ioc=new ClassPathXmlApplicationContext("classpath:spring.xml");

在这里插入图片描述

进入这个获取上下文的构造方法之后,可以发现有调用了这个this方法

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
	this(new String[] {configLocation}, true, null);
}

接下来在进入这个this方法,就是一个熟悉的方法,该方法在前两篇中有所提到。接下来重点就是对里面的前两个方法进行深究,弄清refresh的前置工作到底做了什么

public ClassPathXmlApplicationContext(
	String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
	super(parent); // 初始化父类 ,获得xml路径资源解析器
	setConfigLocations(configLocations); // 通过环境变量解析 xml路径
	if (refresh) {
		refresh(); // 这个方法时spring是最终要的一个方法,甚至体系整个ioc的声明周期
	}
}

1,super(parent)

在该方法中,第一步就是初始化父类,后面很多需要使用的对象,就是在这一步被创建的,而里面的super继续调用自己的super,直到创建一个资源模式处理器,该 AbstractApplicationContext 相对来说比较重要,并且那个最重要的refresh 方法就是在这个抽象类里面

public AbstractApplicationContext() {
    //获取资源模式处理器
	this.resourcePatternResolver = getResourcePatternResolver();
}

接下来就是查看这个具体的获取资源处理器的流程,里面的xml文件,或者其他的注解配置文件,都是能获取的资源,获取到资源之后就对资源进行一个解析操作

protected ResourcePatternResolver getResourcePatternResolver() {
	return new PathMatchingResourcePatternResolver(this);
}

接下来在查看这个 PathMatchingResourcePatternResolver 对象,可以发现里面就是获取资源对象加载器。并且里面还存在一个对象PathMatcher,用做于路径匹配

//用于模式匹配,默认使用的是 PathMatcher
private PathMatcher pathMatcher = new AntPathMatcher();
//获取资源加载器
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
	Assert.notNull(resourceLoader, "ResourceLoader must not be null");
	this.resourceLoader = resourceLoader;
}

在这个ResourceLoader 类中,主要就是两个方法,一个是用于加载资源,一个是用于加载类加载器

//加载资源
Resource getResource(String location);
@Nullable
//加载类加载器
ClassLoader getClassLoader();

在这个 AbstractApplicationContext 构造方法中,完成this获取一个资源解析器之后,接下来就是一个设置一个Parent的父类,当前springIOC中是没有父子容器的概念的,因此到后续的springMVC再进行分析

public AbstractApplicationContext(@Nullable ApplicationContext parent) {
	this();
    //springIOC中暂时没有父子容器概念,先跳过
	setParent(parent);
}

因此这一整个步骤,都是为了初始化成员变量。而最主要的,就是初始化一个资源的解析器。

2,setConfigLocations()

在获取到这个资源解析器之后,接下来就是设置文件的路径。如在正常开发的springboot项目中,通过设置环境的的属性来表名是dev环境还是线上环境等。这个locations参数就是 new String[] {configLocation}

//参数可以是对象或者数组
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;
	}
}

2.1,获取系统属性和系统环境

在获取到外部传进来的文件路径之后,接下来会通过这个 resolvePath方法解析这个路径。而在解析这个路径时,需要通过系统环境变量来解析,如果环境变量为空,则创建一个标准的环境变量

protected String resolvePath(String path) {
    //获取环境
	return getEnvironment().resolveRequiredPlaceholders(path);
}

//如果获取的环境为空,则创建一个标准环境
protected ConfigurableEnvironment createEnvironment() {
	return new StandardEnvironment();
}

而在这些环境中,存在一些spring环境变量的类型,分别是可忽视的,活跃的默认的等

public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";

同时在这个标准环境中,主要分为系统环境和系统属性等

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vp1Jnj13-1686031945306)(img/1686014233433.png)]

//系统环境属性资源名称
static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
//系统配置变量资源名称
static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

最后将全部的系统环境和系统属性一起加入到 propertySources 这个PropertySources集合中,该集合是在父类中实例化的,因此会作为一个全局共享的资源,其子类都能获取和访问

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

加入到集合的value值主要是系统的变量和系统的环境。

//获取系统的属性值
getSystemProperties(){System.getProperties()};
//获取系统的变量
getSystemEnvironment(){System.getenv()};

在创建这个 StandardEnvironment() 标准的环境的时候,可以在父类的无参构造方法中打一个断点,可以发现此时会有两个属性值,就是上面的系统属性值和系统环境值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AhwsdtvS-1686031945307)(img/1685946585512.png)]

而在下面的propertySourceList的第一个值systemProperties中,已经加载了56个系统属性,比如说一些 jdk的版本,虚拟机的版本,操作系统的名称,当前用户的名称等等

在这里插入图片描述

在下面的propertySourceList的第二个值systemEnvironment中,也有49个值,比如说当前电脑的名称,使用的maven路径以及版本,java_home的路径,用户的用户名等等

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JUlyXlED-1686031945308)(img/1685947066781.png)]

此时这些默认的环境对象和环境变量就全被获取。当然这些环境变量的数量也可能因为源码的版本不同个数也会不同。

2.2,解析系统环境和系统属性

又回到上面的第二步,此时环境变量值依旧获取,因此接下来就继续执行这个 resolveRequiredPlaceholders 方法

protected String resolvePath(String path) {
    //获取环境
	return getEnvironment().resolveRequiredPlaceholders(path);
}

在resolveRequiredPlaceholders方法中,会获取到刚刚全部获取到的环境和属性,然后对这些环境和属性做一个解析操作。这里的话类似于一个责任链模式,系统环境要处理的会有对应的方法处理系统环境,系统属性要处理的会有对应的方法处理系统属性。下面这个是处理系统环境的方法

@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
    //this.propertyResolver:全部的系统环境和系统属性
	return this.propertyResolver.resolveRequiredPlaceholders(text);
}

处理完系统环境之后,会再次通过这个责任链模式,去处理对应的系统属性,下面这个是处理系统属性的方法

@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
	if (this.strictHelper == null) {
		this.strictHelper = createPlaceholderHelper(false);
	}
    //解析工作
	return doResolvePlaceholders(text, this.strictHelper);
}

而在处理系统属性时,会有一个 createPlaceholderHelper 方法,类似于一个builder的工厂类

private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
    //前缀,后缀
	return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
		this.valueSeparator, ignoreUnresolvablePlaceholders);
}

在获取到这个strictHelper 对象之后,接下来开始真正的进行解析工作

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

再次进入这个replacePlaceholders 这个方法,可以发现里面会有一个重要的方法parseStringValue

public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
	Assert.notNull(value, "'value' must not be null");
	return parseStringValue(value, placeholderResolver, new HashSet < > ());
}

接下来查看这个 parseStringValue 方法,首先会判断当前的value值中是否包含一个 < / c o d e > 的大括号,并且会递归的判断是否存在 < c o d e > </code> 的大括号,并且会递归的判断是否存在<code> </code>的大括号,并且会递归的判断是否存在<code> 的嵌套,判断完成之后,会对里面的值进行解析。

在这里插入图片描述

在获取完这个 符之后,接着就是递归的循环遍历资源中的 k e y 值,将 < c o d e > 符之后,接着就是递归的循环遍历资源中的key值,将<code> 符之后,接着就是递归的循环遍历资源中的key值,将<code>{USERNAME} 对应的值进行一个替换操作。

String propVal = placeholderResolver.resolvePlaceholder(placeholder);

再次跟着debug断点走,可以发现会进入 PropertySourcesPropertyResolver 类的 getProperty 方法里面

在这里插入图片描述

里面进行循环的取值,将${}里面的值和系统属性或者系统变量的值进行匹配,如果匹配成功,则进行替换的操作

@Nullable
protected < T > T getProperty(String key, Class < T > targetValueType, boolean resolveNestedPlaceholders) {
	if (this.propertySources != null) {
		for (PropertySource << ? > propertySource : this.propertySources) {
            //取值
			Object value = propertySource.getProperty(key);
			if (value != null) {
                //取值成功,则进行替换操作
				if (resolveNestedPlaceholders && value instanceof String) {
					value = resolveNestedPlaceholders((String) value);
				}
				logKeyFound(key, propertySource, value);
                //如果需要的换则进行值转换
				return convertValueIfNecessary(value, targetValueType);
			}
		}
	}
	return null;
}

接下来再进入转换的convertValueIfNecessary 方法,如果不需要转换则直接返回,需要转换则转换

@Nullable
protected < T > T convertValueIfNecessary(Object value, @Nullable Class < T > targetType) {
	if (targetType == null) {
		return (T) value;
	}
	ConversionService conversionServiceToUse = this.conversionService;
	if (conversionServiceToUse == null) {
		if (ClassUtils.isAssignableValue(targetType, value)) {
			return (T) value;
		}
		conversionServiceToUse = DefaultConversionService.getSharedInstance();
	}
    //转换
	return conversionServiceToUse.convert(value, targetType);
}

如已知刚刚获取到的系统环境变量中存在一个 USERNAME=‘PV’,那么假设xml的文件名为 spring-$ {USERNAME}.xml ,那么结果这个解析器进行解析之后,就会将这个${}里面的值进行一个替换操作,会将这个文件名变成 spring-PV.xml 文件。如果存在$的嵌套,那么就会递归的进行一个判断和替换操作, 最终会将解析后的文件返回。

自此为止,属性值就全部加载和解析完成。此时所有的配置文件路径等,都添加在重要的类AbstractRefreshableConfigApplicationContextconfigLocations 的属性里面。

private String[] configLocations;

除了刚刚举例,还有像一些jdbc的连接参数等等,其原理都是一样的,都是通过这种方式替换

"${jdbc.url}")
"${jdbc.driverClassName}"
"${jdbc.username}"
"${jdbc.password}"

3,总结

也就是通过这个xml的方式作为配置文件,在调用refresh方法之前,主要就是做了两件事情:首先是初始化一个资源的解析器,随后是获取系统的属性和系统的环境变量,同时对配置文件的路径进行解析。至此为止,refresh需要准备的前戏工作结束。

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

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

相关文章

【科技素养题】少儿编程 蓝桥杯青少组科技素养题真题及解析第19套

少儿编程 蓝桥杯青少组科技素养题真题及解析第19套 1、下列现象中有化学变化发生的是 A、蜡烛融化 B、冰块融化 C、电磁炉烧开水 D、铁生锈 答案:D 考点分析:主要考查小朋友们的物理和化学知识,题目问的是化学变化;区别物理变化和化学变化的唯一标志是有无新物质生成…

公司来了个新的测试员,本以为是个菜鸡,没想到......

最近公司来了个新同事&#xff0c;学历并不高&#xff0c;而且大学也不是计算机专业的&#xff0c;今年刚满30岁。。 本以为也是来干点基础的活混混日子的&#xff0c;结果没想到这个人上来就把现有项目的性能测试了一遍&#xff0c;直接给公司节省了不少成本&#xff0c;这种…

C语言/C++新手入门学习经验资料分享

一 学好C语言的运算符和运算顺序 这是学好《C程序设计》的基础&#xff0c;C语言的运算非常灵活&#xff0c;功能十分丰富&#xff0c;运算种类远多于其它程序设计语言。 在表达式方面较其它程序语言更为简洁&#xff0c;如自加、自减、逗号运算和三目运算使表达式更为简单&a…

硅谷最爱的测试框架:详解PyTest

Python中有许多测试框架&#xff0c;但其中最受欢迎的就是PyTest。PyTest是一个强大而灵活的测试框架&#xff0c;它提供了许多先进的功能&#xff0c;可以让你的测试更加简洁、易读。 一、PyTest 简介 PyTest是一个开源的Python测试框架&#xff0c;用于编写简单而丰富的测试…

FreeRTOS_任务基础知识

目录 1. 什么是多任务系统&#xff1f; 2. FreeRTOS 任务与协程 2.1 任务 (Task) 的特性 2.2 协程&#xff08;Co - routine&#xff09;的特性 3. 任务状态 4. 任务优先级 5. 任务实现 6. 任务控制块 7. 任务堆栈 RTOS 系统的核心就是任务管理&#xff0c;FreeRTOS 也…

软件测试想要高薪资,不仅要卷还要学会跳槽

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。 这不&#xff0c;前段时间我们公司来了个00后&#xff0c;工作都没两年&#xff0c;跳槽到我们公司起薪20K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了…

C++ new和delete的使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、new和delete介绍二、简单使用1.new和delete2.自定义对象3.new[]和delete[]4.主存耗尽5.try&catch6.nothrow7.看下源代码 前言 new和delete是C里非常重…

【运维】服务器系统安装

目录 一、环境 二、ubuntu 三、启动u盘制作 Stage 1&#xff1a;下载balena&#xff0c;制作U盘启动工具 Stage 2&#xff1a;下载Ubuntu 系统镜像&#xff08;参考上一节&#xff1a;Ubuntu 22.04.2 LTS &#xff09; Stage 3&#xff1a;将镜像写入到U盘 四、设置开启…

6 面阿里、5 面字节、4 面腾讯,可算是入坑了····

8 年前&#xff0c;BAT 冲到了风口浪尖&#xff0c;美国上市的阿里成为中国体量最大的互联网公司&#xff0c;腾讯借助微信成为移动互联网的霸主&#xff0c;外企开始撤离中国&#xff0c;国企的光环也慢慢褪去。 到了近年&#xff0c;应届毕业生心中最炙手可热的公司换成了 T…

共话出海、布局全球,融云WICC2023 · 泛娱乐出海嘉年华广州收官!

&#xff08;移步公众号点击图片三折购买《社交泛娱乐出海作战地图》&#xff09; 6 月 2 日&#xff0c;“WICC 泛娱乐出海嘉年华”在广州成功举办&#xff0c;圆满收官。关注【融云全球互联网通信云】了解更多 本届嘉年华由高端峰会、圆桌会议、露营派对三部分组成&#xf…

资深8年测试,全链路压测与性能的优化详解,一文通透...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 什么是全链路压测…

了解CPU瓶颈原因,掌握代码优化、TOP命令及缓存技术,让服务器不再为性能瓶颈所困扰。

目录 前言&#xff1a; 一、CPU瓶颈原因分析 1. CPU使用率过高 2. 进程使用CPU过多 3. 磁盘I/O读写速度过慢 二、CPU瓶颈调优方案 1. 使用top命令查看CPU使用率 2. 优化程序设计 3. 使用缓存技术 总结&#xff1a; 前言&#xff1a; 在服务器运行过程中&#xff0c;…

干货 | 实战演练基于加密接口测试测试用例设计

如果接口测试仅仅只是掌握一些requests或者其他一些功能强大的库的用法&#xff0c;是远远不够的&#xff0c;还需要具有根据公司的业务以及需求去定制化一个接口自动化测试框架能力。所以在这个部分&#xff0c;会主要介绍接口测试用例分析以及通用的流程封装是如何完成的。 首…

GitHub 竟然有这些骚操作,真是涨姿势

GitHub 竟然有这些骚操作&#xff0c;真是涨姿势 GitHub&#xff0c;不用过多介绍。一个面向开源及私有软件项目的托管平台&#xff0c;因为只支持 git 作为唯一的版本库格式进行托管&#xff0c;故名 GitHub。 作为「全球最大的程序员“交友”社区」&#xff0c;程序员的你&am…

入门AI从谷歌这10门独立课程开始

​ 谷歌最近发布了一个名为"Generative AI learning path"的学习路径&#xff0c;该路径专为初学者设计&#xff0c;共包含10门独立课程。通过这个学习路径&#xff0c;初学者可以从基础概念开始学习&#xff0c;并逐步深入到更复杂的主题&#xff0c;帮助他们了解生…

NLP实战:调用Gensim库训练Word2Vec模型

目录 一、准备工作 1. 安装Gensim库 2. 对原始语料分词 二、训练Word2Vec模型 三、模型应用 1.计算词汇相似度 ​编辑 2. 找出不匹配的词汇 3. 计算词汇的词频 四、总结 &#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学…

第7章:SpringMVC的HttpMessageConverter

1. HttpMessageConverter简介 ①HttpMessageConverter&#xff0c;报文信息转换器&#xff0c;将请求报文转换为java对象&#xff0c;或将java对象转换为响应报文 ②HttpMessageConverter提供了两个注解和两个类型 RequestBody,ResponseBody,RequestEntity,ResponseEntity …

深入详解CFS任务放置代码

一、前言 本文出现的内核代码来自Linux5.10.61&#xff0c;为了减少篇幅&#xff0c;我们对引用的代码进行了删减&#xff08;例如去掉了NUMA的代码&#xff0c;毕竟手机平台上我们暂时不关注这个特性&#xff09;&#xff0c;如果有兴趣&#xff0c;读者可以配合完整的源代码…

Spring Boot JAVA 统一返回的信息

现在的项目是前后端开发的居多&#xff0c;那么&#xff0c;后端的开发只需要返回相关的接口就行了。那么&#xff0c;我们怎么定义接口返回的数据&#xff0c;怎么使用 Spring Boot 来统一处理返回的信息呢&#xff1f; 开发环境如下&#xff1a; IntelliJ IDEA 2021.2.2 (U…

linuxOPS基础_linux权限管理

权限概述 什么是权限 ​ 在多用户计算机系统的管理中&#xff0c;权限是指某个特定的用户具有特定的系统资源使用权利。 在Linux 中分别有读、写、执行权限 \权限针对文件权限针对目录读r(read)表示可以查看文件内容&#xff1b;cat、less…表示可以(ls)查看目录中存在的文…