SpringBoot之OriginTrackedPropertiesLoader类源码学习

news2025/1/15 15:00:55

源码解析

/**
 * 作用是从给定的资源(如文件或输入流)中加载 .properties 文件,
 * 并将属性键值对转换为带有来源信息(origin)的 OriginTrackedValue 对象。
 */
public class OriginTrackedPropertiesLoader {

	private final Resource resource;

	/**
	 * Create a new {@link OriginTrackedPropertiesLoader} instance.
	 * @param resource the resource of the {@code .properties} data
	 */
	public OriginTrackedPropertiesLoader(Resource resource) {
		Assert.notNull(resource, "Resource must not be null");
		this.resource = resource;
	}

	/**
	 * Load {@code .properties} data and return a map of {@code String} ->
	 * {@link OriginTrackedValue}.
	 * @return the loaded properties
	 * @throws IOException on read error
	 */
	public Map<String, OriginTrackedValue> load() throws IOException {
		return load(true);
	}

	/**
	 * 加载配置数据到一个Map中,根据指定的资源文件。
	 * 如果expandLists为true,则将列表类型的配置项展开。
	 *
	 * @param expandLists 是否展开列表类型的配置项
	 * @return 包含配置数据的Map,键为配置项名称,值为配置项的值和来源信息
	 * @throws IOException 如果读取资源文件时发生错误
	 */
	Map<String, OriginTrackedValue> load(boolean expandLists) throws IOException {
		//创建CharacterReader对象,用于逐行读取资源文件内容。
		try (CharacterReader reader = new CharacterReader(this.resource)) {
			//初始化结果Map
			Map<String, OriginTrackedValue> result = new LinkedHashMap<>();
			//初始化字符串缓冲区
			StringBuilder buffer = new StringBuilder();
			//循环读取逐行资源文件内容,直到文件末尾
			while (reader.read()) {
				//读取配置项的键名,并去除前后空格
				String key = loadKey(buffer, reader).trim();
				//如果配置项是列表类型且需要展开列表,则处理列表配置项
				if (expandLists && key.endsWith("[]")) {
					//去除列表配置项的"[]"后缀
					key = key.substring(0, key.length() - 2);
					//初始化列表索引
					int index = 0;
					//循环读取列表中的每个配置项
					do {
						//读取配置项的值
						OriginTrackedValue value = loadValue(buffer, reader, true);
						//将配置项添加到结果Map中,键名格式为key[index]
						put(result, key + "[" + (index++) + "]", value);
						//如果当前行不是行尾,则继续读取
						if (!reader.isEndOfLine()) {
							reader.read();
						}
					} while (!reader.isEndOfLine());
				}
				else {
					//读取非列表配置项的值
					OriginTrackedValue value = loadValue(buffer, reader, false);
					//将配置项添加到结果Map中
					put(result, key, value);
				}
			}
			//返回包含所有配置项的Map
			return result;
		}
	}

	/**
	 * 从输入中加载属性键
	 * 该方法负责读取输入直到遇到属性分隔符或行尾,同时忽略前导和尾随的空白字符
	 *
	 * @param buffer 用于存储读取的键的字符串构建器
	 * @param reader 提供输入字符的字符阅读器
	 * @return 返回读取的属性键的字符串表示
	 * @throws IOException 如果读取过程中发生I/O错误
	 */
	private String loadKey(StringBuilder buffer, CharacterReader reader) throws IOException {
		// 清空缓冲区以准备读取新的键
		buffer.setLength(0);
		// 初始化前一个字符是否为空白字符的标志
		boolean previousWhitespace = false;
		// 循环读取直到行尾
		while (!reader.isEndOfLine()) {
			// 如果遇到属性分隔符,读取下一个字符并返回当前缓冲区的内容
			if (reader.isPropertyDelimiter()) {
				reader.read();
				return buffer.toString();
			}
			// 如果当前字符不是空白字符,但前一个字符是空白字符,则返回当前缓冲区的内容
			if (!reader.isWhiteSpace() && previousWhitespace) {
				return buffer.toString();
			}
			// 更新前一个字符是否为空白字符的标志
			previousWhitespace = reader.isWhiteSpace();
			// 将当前字符追加到缓冲区中
			buffer.append(reader.getCharacter());
			// 读取下一个字符
			reader.read();
		}
		// 如果到达行尾,返回当前缓冲区的内容
		return buffer.toString();
	}


	/**
	 * 从输入中加载一行值,可以选择性地在列表分隔符处分割
	 *
	 * @param buffer 缓存区,用于存储从输入中读取的值
	 * @param reader 字符读取器,用于从输入源读取字符
	 * @param splitLists 指示是否应在遇到列表分隔符时分割的布尔值
	 * @return 返回一个包含值及其来源信息的OriginTrackedValue对象
	 * @throws IOException 如果在读取过程中发生I/O错误
	 */
	private OriginTrackedValue loadValue(StringBuilder buffer, CharacterReader reader, boolean splitLists)
			throws IOException {
		// 清空缓冲区以准备读取新的值
		buffer.setLength(0);
		// 跳过行首的空白字符,直到遇到非空白字符或行尾
		while (reader.isWhiteSpace() && !reader.isEndOfLine()) {
			reader.read();
		}
		// 记录当前读取位置,用于后续创建Origin对象
		Location location = reader.getLocation();
		// 读取字符直到行尾或(如果splitLists为真)遇到列表分隔符
		while (!reader.isEndOfLine() && !(splitLists && reader.isListDelimiter())) {
			buffer.append(reader.getCharacter());
			reader.read();
		}
		// 创建一个表示值来源的Origin对象
		Origin origin = new TextResourceOrigin(this.resource, location);
		// 使用缓冲区中的字符串和其来源信息创建并返回一个OriginTrackedValue对象
		return OriginTrackedValue.of(buffer.toString(), origin);
	}


	/**
	 * Reads characters from the source resource, taking care of skipping comments,
	 * handling multi-line values and tracking {@code '\'} escapes.
	 * 用于逐字符读取文件内容,处理注释、转义字符、多行值等特殊情况。
	 * 提供了多种辅助方法来跳过空白字符、处理注释、读取转义字符等。
	 */
	private static class CharacterReader implements Closeable{
	//省略......
	}

}

案例

test-properties.properties配置文件

   # foo
blah   =   hello world
bar   foo=baz
hello   world
proper\\ty=test
foo
bat = a\\
bling = a=b

#commented-property=test
test=properties
test-unicode=properties\u0026test
  # comment ending \
test\=property=helloworld
test-colon-separator: my-property
test-tab-property=foo\tbar
test-return-property=foo\rbar
test-newline-property=foo\nbar
test-form-feed-property=foo\fbar
test-whitespace-property   =   foo   bar
test-multiline= a\
  b\\\
  c
foods[]=Apple,\
Orange,\
Strawberry,\
Mango
languages[perl]=Elite
languages[python]=Elite
language[pascal]=Lame
test-multiline-immediate=\
foo
!commented-two=bang\
test-bang-property=foo!
another=bar
test-property-value-comment=foo \
!bar #foo
test-multiline-immediate-bang=\
!foo

#test ISO 8859-1
test-iso8859-1-chars=����������

test-with-trailing-space= trailing 
	private ClassPathResource resource;

	private Map<String, OriginTrackedValue> properties;
	
	@Test
	void compareToJavaProperties() throws Exception {
		String path = "test-properties.properties";
		this.resource = new ClassPathResource(path, getClass());
		this.properties = new OriginTrackedPropertiesLoader(this.resource).load();
		Properties java = PropertiesLoaderUtils.loadProperties(this.resource);
		Properties ours = new Properties();
		new OriginTrackedPropertiesLoader(this.resource)
				.load(false)
				.forEach((k, v) -> System.out.println(k+":"+v.getValue()));
	}

运行结果

blah:hello world
bar:foo=baz
hello:world
proper\ty:test
foo:
bat:a\
bling:a=b
test:properties
test-unicode:properties&test
test=property:helloworld
test-colon-separator:my-property
test-tab-property:foo	bar
bar
test-newline-property:foo
bar
test-form-feed-property:foobar
test-whitespace-property:foo   bar
test-multiline:ab\c
foods[]:Apple,Orange,Strawberry,Mango
languages[perl]:Elite
languages[python]:Elite
language[pascal]:Lame
test-multiline-immediate:foo
test-bang-property:foo!
another:bar
test-property-value-comment:foo !bar #foo
test-multiline-immediate-bang:!foo
test-iso8859-1-chars:����������
test-with-trailing-space:trailing 

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

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

相关文章

重生之我在21世纪学C++—函数与递归

一、函数是什么&#xff1f; 相信我们第一次接触函数是在学习数学的时候&#xff0c;比如&#xff1a;一次函数 y kx b &#xff0c;k 和 b 都是常数&#xff0c;给一个任意的 x &#xff0c;就会得到一个 y 值。 其实在 C 语言中就引入了函数&#xff08;function&#xf…

Mac——Cpolar内网穿透实战

摘要 本文介绍了在Mac系统上实现内网穿透的方法&#xff0c;通过打开远程登录、局域网内测试SSH远程连接&#xff0c;以及利用cpolar工具实现公网SSH远程连接MacOS的步骤。包括安装配置homebrew、安装cpolar服务、获取SSH隧道公网地址及测试公网连接等关键环节。 1. MacOS打开…

Ubuntu中双击自动运行shell脚本

方法1: 修改文件双击反应 参考: https://blog.csdn.net/miffywm/article/details/103382405 chmod x test.sh鼠标选中待执行文件&#xff0c;在窗口左上角edit菜单中选择preference设计双击执行快捷键&#xff0c;如下图&#xff1a; 方法2: 设置一个应用 参考: https://blo…

力扣 全排列

回溯经典例题。 题目 通过回溯生成所有可能的排列。每次递归时&#xff0c;选择一个数字&#xff0c;直到选满所有数字&#xff0c;然后记录当前排列&#xff0c;回到上层时移除最后选的数字并继续选择其他未选的数字。每次递归时&#xff0c;在 path 中添加一个新的数字&…

arcgis提取不规则栅格数据的矢量边界

效果 1、准备数据 栅格数据:dem或者dsm 2、栅格重分类 分成两类即可 3、新建线面图层 在目录下选择预先准备好的文件夹,点击右键,选择“新建”→“Shapefile”,新建一个Shapefile文件。 在弹出的“新建Shapefile”对话框内“名称”命名为“折线”,“要素类型”选…

【DB-GPT】开启数据库交互新篇章的技术探索与实践

一、引言&#xff1a;AI原生数据应用开发的挑战与机遇 在数字化转型的浪潮中&#xff0c;企业对于智能化应用的需求日益增长。然而&#xff0c;传统的数据应用开发方式面临着诸多挑战&#xff0c;如技术栈复杂、开发周期长、成本高昂、难以维护等。这些问题限制了智能化应用的…

LVGL移植高通点阵字库GT30L24A3W

字库芯片: GT30L24A3W MCU:STM32F429 LVGL版本:V8.4 一、实现gt_read_data() 和 r_dat_bat() 请参考下面视频 如何在32位MCU上使用高通点阵字库_哔哩哔哩_bilibili 高通字库使用教程(1)硬件链接与注意事项部分_哔哩哔哩_bilibili 高通字库使用教程(2)SPI底层函数使用_哔哩…

一键掌握多平台短视频矩阵营销/源码部署

短视频矩阵系统的介绍与应用 随着数字化营销策略的不断演进&#xff0c;传统的短视频矩阵操作方法可能已显陈旧。为此&#xff0c;一款全新的短视频矩阵系统应运而生&#xff0c;它通过整合多个社交媒体账户、创建多样化的任务、运用先进的智能视频编辑工具、实现多平台内容的…

MySQL(高级特性篇) 06 章——索引的数据结构

一、为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构&#xff0c;就好比一本教科书的目录部分&#xff0c;通过目录找到对应文章的页码&#xff0c;便可快速定位到需要的文章。MySQL中也是一样的道理&#xff0c;进行数据查找时&#xff0c;首先查看查询条件…

源码安装httpd2.4

1、下载 wget https://archive.apache.org/dist/httpd/httpd-2.4.54.tar.gz 2.解压下载压缩包 tar -zxvf httpd-2.4.54.tar.gz cd httpd-2.4.54 3、安装httpd所需要的依赖 yum groupinstall "Development Tools" -y 4.配置httpd ./configure --prefix/usr/local/htt…

【算法学习】——整数划分问题详解(动态规划)

&#x1f9ee;整数划分问题是一个较为常见的算法题&#xff0c;很多问题从整数划分这里出发&#xff0c;进行包装&#xff0c;形成新的题目&#xff0c;所以完全理解整数划分的解决思路对于之后的进一步学习算法是很有帮助的。 「整数划分」通常使用「动态规划」解决&#xff0…

文件与IO流:一

一些常识 硬盘特点 擅长顺序读&#xff0c;不擅长随机读&#xff0c;尤其是机械硬盘。 随机读例如某个目录中的所有小文件的复制&#xff0c;顺序读是某个大文件的整体复制。 windows的文件系统是按照“树形结构”来组织文件。 路径的风格 1.绝对路径&#xff1a;从根节点…

计算机网络 (42)远程终端协议TELNET

前言 Telnet&#xff08;Telecommunication Network Protocol&#xff09;是一种网络协议&#xff0c;属于TCP/IP协议族&#xff0c;主要用于提供远程登录服务。 一、概述 Telnet协议是一种远程终端协议&#xff0c;它允许用户通过终端仿真器连接到远程主机&#xff0c;并在远程…

WPF系列十二:图形控件CombinedGeometry

简介 CombinedGeometry 是 WPF (Windows Presentation Foundation) 中的一个几何对象&#xff0c;用于将两个几何图形组合成一个新的几何图形。它允许你通过不同的组合模式&#xff08;如相交、并集、差集或异或&#xff09;来创建复杂的形状。常与 Path 控件一起使用来绘制组…

《计算机网络》课后探研题书面报告_网际校验和算法

网际校验和算法 摘 要 本文旨在研究和实现网际校验和&#xff08;Internet Checksum&#xff09;算法。通过阅读《RFC 1071》文档理解该算法的工作原理&#xff0c;并使用编程语言实现网际校验和的计算过程。本项目将对不同类型的网络报文&#xff08;包括ICMP、TCP、UDP等&a…

业务幂等性技术架构体系之接口幂等深入剖析

在实际应用中&#xff0c;由于网络不稳定、系统延迟等原因&#xff0c;客户端可能会重复发送相同的请求。如果这些重复请求都被服务器处理并执行&#xff0c;就可能导致意想不到的问题&#xff0c;比如重复扣款、多次下单或者数据不一致等。 这就是为什么我们需要接口幂等性。…

sql模糊关联匹配

需求目标&#xff1a; 建立临时表 drop table grafana_bi.zbj_gift_2024;USE grafana_bi; CREATE TABLE zbj_gift_2024 (id INT AUTO_INCREMENT PRIMARY KEY,userName VARCHAR(255),giftName VARCHAR(255),giftNum INT,points INT,teacher VARCHAR(255),sendDate DATETIME,…

《蜜蜂路线》

题目背景 无 题目描述 一只蜜蜂在下图所示的数字蜂房上爬动,已知它只能从标号小的蜂房爬到标号大的相邻蜂房,现在问你&#xff1a;蜜蜂从蜂房 mm 开始爬到蜂房 nn&#xff0c;m<nm<n&#xff0c;有多少种爬行路线&#xff1f;&#xff08;备注&#xff1a;题面有误&am…

LeetCode100之搜索二维矩阵(46)--Java

1.问题描述 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回…

JS爬虫实战演练

在这个小红书私信通里面进行一个js的爬虫 文字发送 async function sendChatMessage(content) {const url https://pro.xiaohongshu.com/api/edith/ads/pro/chat/chatline/msg;const params new URLSearchParams({porch_user_id: 677e116404ee000000000001});const messageD…