Sping源码(七)—ConfigurationClassPostProcessor ——@PropertySources解析

序言

先来简单回顾一下ConfigurationClassPostProcessor大致的一个处理流程,再来详细的讲解@PropertySources注解的处理逻辑。
详细的步骤可参考ConfigurationClassPostProcessor这篇帖子。

流程图
从获取所有BeanDefinition -> 过滤、赋值、遍历 -> 解析 -> 优先递归处理内部类这一系列操作后。
来到了最后一步,处理@Bean、@Configuration、@Component、@ComponentScan、@PropertySource等注解。
按照代码的执行顺序,首先介绍@PropertySource注解的执行原理。
在这里插入图片描述

@PropertySource

测试类
创建类MyService并添加@Configuration和@PropertySource。
其中@Configuration继承了@Component , 而@PropertySource引入了myconfig.properties文件。

@Configuration
@PropertySource({"classpath:myconfig.properties"})
public class MyService{
}

Other同样加载了myconfig.properties文件

@Component
@PropertySource({"classpath:myconfig.properties"})
public class Other {

}

myconfig.properties
文件中简单的设置一个name属性即可。

myconfig.name=lisi

流程图
在这里插入图片描述

源码片段
来看看加载后解析@PropertySource时做了什么。

protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		//如果是@Component注解,则优先递归处理内部类
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass, filter);
		}

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

	//省略部分源码 。。。
	}

获取注解中属性

获取注解中我们定义的name、encoding、value等属性值,根据我们定义的java类中只有value有具体的属性值。
获取value属性后转换成Resource对象,并再次封装成ResourcePropertySource对象用来存储properties文件属性。

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
		//获取name属性值
		String name = propertySource.getString("name");
		if (!StringUtils.hasLength(name)) {
			name = null;
		}
		//获取encoding属性值
		String encoding = propertySource.getString("encoding");
		if (!StringUtils.hasLength(encoding)) {
			encoding = null;
		}
		//获取value属性值
		String[] locations = propertySource.getStringArray("value");
		Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
		boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

		Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
		PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
				DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
		//遍历location
		for (String location : locations) {
			try {
				String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
				Resource resource = this.resourceLoader.getResource(resolvedLocation);
				addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
			}
		
		//省略catch捕获异常部分源码。。
		}
	}
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
		return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
	}

其中遍历location部分源码是否有些熟悉?
同样是String -> Resource的转换。refresh()主流程方法中将xml文件也是将String[] -> String -> Resource[] -> Resource。

首次加载

将文件封装成ResourcePropertySource对象后,如果该文件未被加载过则添加到propertySources属性尾端,否则封装成CompositePropertySource对象。
值得注意的是,目前只是对文件进行加载,没有对文件中字段属性做特别处理。

addPropertySource
我们第一次加载处理MyService类中@PropertySource注解时,propertySourceNames属性的size = 0, 所以会直接添加到propertySources的尾端。

private void addPropertySource(PropertySource<?> propertySource) {
		String name = propertySource.getName();
		MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();

		//如果资源文件名已经存在,则进行扩展
		if (this.propertySourceNames.contains(name)) {
			// We've already added a version, we need to extend it
			//获取已经存在的资源文件
			PropertySource<?> existing = propertySources.get(name);
			if (existing != null) {
				//获取扩展的资源文件
				PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
						((ResourcePropertySource) propertySource).withResourceName() : propertySource);
				//如果资源文件是CompositePropertySource,则添加到CompositePropertySource中
				if (existing instanceof CompositePropertySource) {
					((CompositePropertySource) existing).addFirstPropertySource(newSource);
				}
				else {
					//如果资源文件不是CompositePropertySource,则创建CompositePropertySource
					if (existing instanceof ResourcePropertySource) {
						existing = ((ResourcePropertySource) existing).withResourceName();
					}
					CompositePropertySource composite = new CompositePropertySource(name);
					composite.addPropertySource(newSource);
					composite.addPropertySource(existing);
					propertySources.replace(name, composite);
				}
				return;
			}
		}

		// 如果没有已处理的属性源,将新属性源添加到末尾;如果有,则在最后一个处理的属性源之前添加
		if (this.propertySourceNames.isEmpty()) {
			propertySources.addLast(propertySource);
		}
		else {
			String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
			propertySources.addBefore(firstProcessed, propertySource);
		}
		this.propertySourceNames.add(name);
	}

因为我们只设置了@PropertySource中的value属性,所以resourceName为null。

public ResourcePropertySource withResourceName() {
		if (this.resourceName == null) {
			return this;
		}
		return new ResourcePropertySource(this.resourceName, null, this.source);
	}

而最后添加的propertySources属性,我们debug看一下它里面都有什么。
在这里插入图片描述
可以看到,我们的myconfig.properties文件已经加到了里面,并且source中有我们设置的myconfig.name字段。
而前两个systemProperties和systemEnvironment属性在之前帖子中也有讲到。是我们StandardEnvironment对象创建时获取到的系统变量。

在父类AbstractEnvironment构造方法中 进行调用。

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

public class StandardEnvironment extends AbstractEnvironment {

	/** System environment property source name: {@value}. */
	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

	/** JVM system properties property source name: {@value}. */
	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

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

}

再次加载

MyService类对应的@PropertySource已经处理完成,再来看看Other类中的@PropertySource注解加载时都做了什么?

源码片段
依然是addPropertySource()方法没有变,此时要加载的资源文件都是myconfig.properties。所以propertySourceNames属性不为null。
代码逻辑会走if判断并将之前加载过的资源文件(existing)和新封装的资源文件propertySource(myconfig.properties)封装到CompositePropertySource,并根据name计算索引替换propertySources原位置的propertySource。

private void addPropertySource(PropertySource<?> propertySource) {
		String name = propertySource.getName();
		MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();

		//如果资源文件名已经存在,则进行扩展
		if (this.propertySourceNames.contains(name)) {
			// We've already added a version, we need to extend it
			//获取已经存在的资源文件
			PropertySource<?> existing = propertySources.get(name);
			if (existing != null) {
				//获取扩展的资源文件
				PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
						((ResourcePropertySource) propertySource).withResourceName() : propertySource);
				//如果资源文件是CompositePropertySource,则添加到CompositePropertySource中
				if (existing instanceof CompositePropertySource) {
					((CompositePropertySource) existing).addFirstPropertySource(newSource);
				}
				else {
					//如果资源文件不是CompositePropertySource,则创建CompositePropertySource
					if (existing instanceof ResourcePropertySource) {
						existing = ((ResourcePropertySource) existing).withResourceName();
					}
					CompositePropertySource composite = new CompositePropertySource(name);
					composite.addPropertySource(newSource);
					composite.addPropertySource(existing);
					propertySources.replace(name, composite);
				}
				return;
			}
		}

		// 如果没有已处理的属性源,将新属性源添加到末尾;如果有,则在最后一个处理的属性源之前添加
		if (this.propertySourceNames.isEmpty()) {
			propertySources.addLast(propertySource);
		}
		else {
			String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
			propertySources.addBefore(firstProcessed, propertySource);
		}
		this.propertySourceNames.add(name);
	}

以上就是@PropertySource注解解析的全过程。需要注意的是!!!
此时仅仅是文件的加载,并未对文件中属性、字段做任何逻辑处理。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

璞华科技中标苏州工业园区“科技发展公司运营管理系统”升级改造项目

近日&#xff0c;璞华科技中标苏州工业园区科技发展有限公司“科技发展公司运营管理系统”升级改造项目。 苏州工业园区科技发展有限公司成立于2000年&#xff0c;是苏州工业园区管委会直属国有企业&#xff0c;聚焦以人工智能为引领的数字经济产业创新集群&#xff0c;重点布局…

Web自动化 - selenium

文章目录 一、selenium的使用selenium的安装 二、元素1. 定位选择元素1.id 定位2. class_name 定位find_element 和 find_elements的区别3. TAG_NAME 定位4. 超链接 定位 2. 操控元素1. 查询内容2. 获取元素文本内容3. 获取元素属性 3. 浏览器常用操作API4. 鼠标操作 - perform…

17 M-LAG 配置思路

16 华三数据中心最流行的技术 M-LAG-CSDN博客 M-LAG 配置思路 什么是M-LAG&#xff1f;为什么需要M-LAG&#xff1f; - 华为 (huawei.com) 1 配置 M-LAG 的固定的MAC地址 [SW-MLAG]m-lag system-mac 2-2-2 2 配置M-LAG 的系统标识符系统范围1到2 [SW-MLAG]m-lag system-nu…

【算法】动态规划之线性DP问题

前言&#xff1a; 本系列是看的B站董晓老师所讲的知识点做的笔记 董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (bilibili.com) 树塔-记忆化搜索 特点&#xff08;前提&#xff09;&#xff1a;从上向下的累加和是不能重复使用的&#xff0c;从下向上的累加和是可以重…

人民币汇率相关历史数据集(2006-2022年)

01、数据简介 汇率指的是两种货币之间兑换的比率&#xff0c;亦可视为一个国家的货币对另一种货币的价值。具体来说&#xff0c;汇率亦可视为一个国家的两种货币之间所存在的兑换比率&#xff0c;亦可视为一个国家的货币对另一种货币的价值。汇率的变动对一国的进出口贸易有着…

k8s 数据流向 与 核心概念详细介绍

目录 一 k8s 数据流向 1&#xff0c;超级详细版 2&#xff0c;核心主键及含义 3&#xff0c;K8S 创建Pod 流程 4&#xff0c;用户访问流程 二 Kubernetes 核心概念 1&#xff0c;Pod 1.1 Pod 是什么 1.2 pod 与容器的关系 1.3 pod中容器 的通信 2&#xff0c; …

94、动态规划-最长公共子序列

递归的基本思路&#xff1a; 比较两个字符串的最后一个字符。如果相同&#xff0c;则这个字符一定属于最长公共子序列&#xff0c;然后在剩余的字符串上递归求解。如果最后一个字符不相同&#xff0c;则分两种情况递归求解&#xff1a; 去掉 text1 的最后一个字符&#xff0c;保…

中国当代最具影响力的人物颜廷利:死神(死亡)并不可怕,可怕的是…

中国当代最具影响力的人物颜廷利&#xff1a;死神&#xff08;死亡&#xff09;并不可怕&#xff0c;可怕的是… 在中国优秀传统文化之中&#xff0c;汉语‘巳’字与‘四’同音&#xff0c;在阿拉伯数字里面&#xff0c;通常用‘4’来表示&#xff1b; 作为汉语‘九’字&#x…

mysql设置远程访问权限,允许其他IP访问

文章目录 更改mysql配置文件登录mysql 更改mysql配置文件 查找.ini或者.cnf文件 更改bind-address为0.0.0.0 [mysqld] character-set-serverutf8mb4 bind-address0.0.0.0 default-storage-engineINNODB [mysql] default-character-setutf8mb4 [client] default-character-s…

【getopt函数用法】

这里写目录标题 一、概述二、选项字符串规则&#xff1a;三、getopt 返回值四、会用到的全局变量&#xff1a;三、示例代码四、上机实验 一、概述 int getopt(int argc, char * const argv[], const char *optstring); extern char *optarg; //这个最常用&#xff0c;保存一个…

conan2 基础入门(06)-conanfile.py入门

conan2 基础入门(06)-conanfile.py入门 文章目录 conan2 基础入门(06)-conanfile.py入门⭐准备预备文件和Code ⭐使用流程指令 ⭐具体讲解conanfile.pyconan install END视频教学 ⭐准备 注意&#xff0c;如果想跟好的学习conanfile.py建议使用python来安装conan。 当然使用其…

C++之STL-priority_queue和仿函数的讲解

目录 一、priority_queue的介绍和使用 1.1 priority_queue的介绍 1.2 priority_queue的基本接口 二、仿函数的介绍 2.1 基本概念 2.2 适用场景 三、模拟实现priority_queue 3.1 向上调整算法 3.2 向下调整算法 3.3 整体框架 一、priority_queue的介绍和使用 1.1 prio…

U盘文件遇损?拯救“文件或目录损坏且无法读取”的秘籍!

在数字化时代&#xff0c;U盘已成为我们日常生活与工作中不可或缺的数据存储和传输工具。然而&#xff0c;有时我们可能会遇到一个非常令人沮丧的问题——U盘中的文件或目录突然损坏且无法读取。这种突发状况往往让人措手不及&#xff0c;甚至可能引发数据丢失的严重后果。那么…

【基于 PyTorch 的 Python 深度学习】6 视觉处理基础:卷积神经网络(2)

前言 文章性质&#xff1a;学习笔记 &#x1f4d6; 学习资料&#xff1a;吴茂贵《 Python 深度学习基于 PyTorch ( 第 2 版 ) 》【ISBN】978-7-111-71880-2 主要内容&#xff1a;根据学习资料撰写的学习笔记&#xff0c;该篇主要介绍了卷积神经网络的池化层部分和现代经典网络。…

2022CSP-S易错题解析

21.B i的取值分别是0、5、6、7、8、13&#xff0c;其中i5时&#xff0c;j运行3次&#xff1b;i6时&#xff0c;j运行2次&#xff1b;i7时&#xff0c;j运行1次&#xff1b;i13时&#xff0c;j运行4次。共10次。 25.D 第1次执行时&#xff0c;数字是按照三进制下的最低位从小到…

【C++】——string类

前言 在C语言里面我们用的字符串都是以\0结尾的字符合集&#xff0c;为了操作方便所以在c中推出了stirng类 一 string介绍 1.string是表示字符串的字符串类 2.因为是类&#xff0c;所以他会有一些常用的接口&#xff0c;同时也添加了专门用来操作string的常规操作 3.string…

什么是$t?$t的介绍及使用

目录 $t介绍&#xff1a; 作用&#xff1a; 安装国际化插件&#xff1a; 创建国际化资源文件 配置 vue-i18n &#xff1a; 切换语言&#xff1a; 下面为中文和英文状态下的效果&#xff1a; 如下面所示&#xff0c;这是一段前端代码&#xff1a; <el-form-item :label…

基于短时傅里叶变换域的一维信号邻域降噪方法(MATLAB)

基于傅里叶变换的信号频域表示及能量频域分布揭示了信号在频域的特征&#xff0c;但傅里叶变换是一种整体变换&#xff0c;只能了解信号的全局特性&#xff0c;不能有效检测信号频率随时间的变化情况。只有把时域和频域结合起来才能更好地反映非平稳信号的特征。时频分析的基本…

Redis 的主从复制

Redis 的主从复制 1、主从复制的实现2、主从复制的同步功能(PSYNC)2.1、部分重同步 本文讲解的Redis 主从复制机制&#xff0c;是基于 2.8及以后的版本而言&#xff0c;2.8以前的版本主从复制机制与此有所不同&#xff0c;请知悉。 Redis的复制功能分为 同步 (psync) 和 命令传…

远程点击没反应

目录 todesk远程登录后点击没反应 解决方法&#xff1a; 方法1 快捷键&#xff1a; 方法2 界面点击Ctrl Alt Delete todesk&#xff0c;向日葵远程登录后点击没反应 todesk远程登录后点击没反应 解决方法&#xff1a; 方法1 快捷键&#xff1a; Ctrl Alt Delete 方法…