SpringBoot之OriginTrackedPropertiesLoader类源码学习

embedded/2025/1/16 16:41:58/

源码解析

java">/*** 作用是从给定的资源(如文件或输入流)中加载 .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)) {//初始化结果MapMap<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);}}//返回包含所有配置项的Mapreturn 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 
java">	private ClassPathResource resource;private Map<String, OriginTrackedValue> properties;@Testvoid 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.ppmy.cn/embedded/154429.html

相关文章

1,Linux环境变量基本定义(基于Ubuntu示例进行讲解)

linux环境变量的概念 Linux环境变量&#xff08;准确说应该是shell变量&#xff09;&#xff0c;是直接存储在操作系统中的一组键值对&#xff08;dict类型&#xff09;&#xff0c;用于配置系统和应用程序的操作行为。 【有经验的描述】&#xff1a;它们的工作原理很简单&am…

Shell作业二

1、编写一个Shell脚本用于判断192.168.242.0/24网络中当前在线的IP地址&#xff0c;并打印出这些IP地址。 脚本&#xff1a; #!/bin/bash# 网络前缀 NETWORK"192.168.242"echo "正在扫描网络 $NETWORK.0/24 中的活动主机..."# 遍历主机地址 for i in {1..…

JavaScript系列(26)--安全编程实践详解

JavaScript安全编程实践详解 &#x1f512; 今天&#xff0c;让我们深入探讨JavaScript的安全编程实践。在当今的网络环境中&#xff0c;安全性已经成为开发者必须重点关注的领域。 安全编程基础 &#x1f31f; &#x1f4a1; 小知识&#xff1a;JavaScript安全编程涉及多个方…

[石榴翻译] 维吾尔语音识别 + TTS语音合成

API网址 丝路AI平台 获取 Access token 接口地址&#xff1a;https://open.xjguoyu.cn/api/auth/oauth/token&#xff0c;请求方式&#xff1a;GET&#xff0c;POST Access token是调用服务API的凭证&#xff0c;调用服务API之前需要获取 token。每次成功获取 token 以后只有…

解决 Git SSL 连接错误:OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno

问题描述 在执行 git pull 命令时遇到以下错误&#xff1a; > git pull --tags origin main fatal: unable to access github仓库: OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 0这个错误通常表示 Git 在尝试通过 HTTPS 连接到 GitHub 时遇到了 SSL 连接问题。 解决方案…

es,单个节点磁盘使用率高

背景&#xff1a; 磁盘使用率不均匀&#xff0c;一般是因为存在大分片&#xff0c;分片数和机器数不匹配引起的。 这次出现的问题排除了&#xff0c;分片问题。 一个节点使用到87%&#xff0c; 其它节点60% 左右&#xff0c; 原因&#xff1a; 是因为升级配置数据迁移的时候 迁…

干净卸载Windows的Node.js环境的方法

本文介绍在Windows电脑中&#xff0c;彻底删除Node.js环境的方法。 在之前的文章Windows系统下载、部署Node.js与npm环境的方法&#xff08;https://blog.csdn.net/zhebushibiaoshifu/article/details/144810076&#xff09;中&#xff0c;我们介绍过在Windows电脑中&#xff0…

基于STM32设计的粮食仓库(粮仓)环境监测系统

一、前言 1.1 项目开发背景 随着现代农业的发展和粮食储存规模的扩大&#xff0c;粮仓环境的智能化监控需求日益增长。传统的粮仓管理方式通常依赖人工检测和定期巡查&#xff0c;效率低下且容易出现疏漏&#xff0c;无法及时发现潜在问题&#xff0c;可能导致粮食受潮、霉变…