【Java开发日记】你会不会5种牛犇的yml文件读取方式?

前言
除了烂大街的@Value@ConfigurationProperties外,还能够通过哪些方式,来读取yml配置文件的内容?


1、Environment

在Spring中有一个类Environment,它可以被认为是当前应用程序正在运行的环境,它继承了PropertyResolver接口,因此可以作为一个属性解析器使用。先创建一个yml文件,属性如下:

person:name: hydragender: maleage: 18

使用起来也非常简单,直接使用@Autowired就可以注入到要使用的类中,然后调用它的getProperty()方法就可以根据属性名称取出对应的值了。

@RestController
public class EnvironmentController {@Autowiredprivate Environment environment;@GetMapping("envTest")private void getEnv(){System.out.println(environment.getProperty("person.name"));System.out.println(environment.getProperty("person.gender"));Integer autoClose = environment.getProperty("person.age", Integer.class);System.out.println(autoClose);String defaultValue = environment.getProperty("person.other", String.class, "defaultValue");System.out.println(defaultValue);}
}

在上面的例子中可以看到,除了简单的获取外,Environment提供的方法还可以对取出的属性值进行类型转换、以及默认值的设置,调用一下上面的接口,打印结果如下:

hydra
male
18
defaultValue

除了获取属性外,还可以用来判断激活的配置文件,先在application.yml中激活pro文件:

spring:profiles:active: pro

可以通过acceptsProfiles方法来检测某一个配置文件是否被激活加载,或者通过getActiveProfiles方法拿到所有被激活的配置文件。测试接口:

@GetMapping("getActiveEnv")
private void getActiveEnv(){System.out.println(environment.acceptsProfiles("pro"));System.out.println(environment.acceptsProfiles("dev"));String[] activeProfiles = environment.getActiveProfiles();for (String activeProfile : activeProfiles) {System.out.println(activeProfile);}
}

打印结果:

true
false
pro

2、YamlPropertiesFactoryBean

在Spring中还可以使用YamlPropertiesFactoryBean来读取自定义配置的yml文件,而不用再被拘束于application.yml及其激活的其他配置文件。
在使用过程中,只需要通过setResources()方法设置自定义yml配置文件的存储路径,再通过getObject()方法获取Properties对象,后续就可以通过它获取具体的属性,下面看一个例子:

@GetMapping("fcTest")
public void ymlProFctest(){YamlPropertiesFactoryBean yamlProFb = new YamlPropertiesFactoryBean();yamlProFb.setResources(new ClassPathResource("application2.yml"));Properties properties = yamlProFb.getObject();System.out.println(properties.get("person2.name"));System.out.println(properties.get("person2.gender"));System.out.println(properties.toString());
}

查看运行结果,可以读取指定的application2.yml的内容:

fcant
female
{person2.age=18, person2.gender=female, person2.name=fcant}

但是这样的使用中有一个问题,那就是只有在这个接口的请求中能够取到这个属性的值,如果再写一个接口,不使用YamlPropertiesFactoryBean读取配置文件,即使之前的方法已经读取过这个yml文件一次了,第二个接口取到的仍然还是空值。来对这个过程进行一下测试:

@Value("${person2.name:null}")
private String name;
@Value("${person2.gender:null}")
private String gender;
@GetMapping("fcTest2")
public void ymlProFctest2(){System.out.println(name);System.out.println(gender);
}

先调用一次fcTest接口,再调用fcTest2接口时会打印null值:

null
null

想要解决这个问题也很简单,可以配合PropertySourcesPlaceholderConfigurer使用,它实现了BeanFactoryPostProcessor接口,也就是一个bean工厂后置处理器的实现,可以将配置文件的属性值加载到一个Properties文件中。使用方法如下:

@Configuration
public class PropertyConfig {@Beanpublic static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();YamlPropertiesFactoryBean yamlProFb = new YamlPropertiesFactoryBean();yamlProFb.setResources(new ClassPathResource("application2.yml"));configurer.setProperties(yamlProFb.getObject());return configurer;}
}

再次调用之前的接口,结果如下,可以正常的取到application2.yml中的属性:

fcant
female

除了使用YamlPropertiesFactoryBean将yml解析成Properties外,其实还可以使用YamlMapFactoryBean解析yml成为Map,使用方法非常类似:

@GetMapping("fcMapTest")
public void ymlMapFctest(){YamlMapFactoryBean yamlMapFb = new YamlMapFactoryBean();yamlMapFb.setResources(new ClassPathResource("application2.yml"));Map<String, Object> map = yamlMapFb.getObject();System.out.println(map);
}

打印结果:

{person2={name=fcant, gender=female, age=18}}

3、监听事件

SpringBoot是通过监听事件的方式来加载和解析的yml文件,那么也可以仿照这个模式,来加载自定义的配置文件。
首先,定义一个类实现ApplicationListener接口,监听的事件类型为ApplicationEnvironmentPreparedEvent,并在构造方法中传入要解析的yml文件名:

public class YmlListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {private String ymlFilePath;public YmlListener(String ymlFilePath){this.ymlFilePath = ymlFilePath;}//...
}

自定义的监听器中需要实现接口的onApplicationEvent()方法,当监听到ApplicationEnvironmentPreparedEvent事件时会被触发:

@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {ConfigurableEnvironment environment = event.getEnvironment();ResourceLoader loader = new DefaultResourceLoader();YamlPropertySourceLoader ymlLoader = new YamlPropertySourceLoader();try {List<PropertySource<?>> sourceList = ymlLoader.load(ymlFilePath, loader.getResource(ymlFilePath));for (PropertySource<?> propertySource : sourceList) {environment.getPropertySources().addLast(propertySource);}} catch (IOException e) {e.printStackTrace();}
}

上面的代码中,主要实现了:

  • 获取当前环境Environment,当ApplicationEnvironmentPreparedEvent事件被触发时,已经完成了Environment的装载,并且能够通过event事件获取
  • 通过YamlPropertySourceLoader加载、解析配置文件
  • 将解析完成后的OriginTrackedMapPropertySource添加到Environment

修改启动类,在启动类中加入这个监听器:

public static void main(String[] args) {SpringApplication application = new SpringApplication(MyApplication.class);application.addListeners(new YmlListener("classpath:/application2.yml"));application.run(args);
}

在向environment中添加propertySource前加一个断点,查看环境的变化。
执行完成后,可以看到配置文件源已经被添加到了环境中。
启动完成后再调用一下接口,查看结果:

Fcant
female

能够正确的取到配置文件中的值,说明自定义的监听器已经生效。 

4、SnakeYml

前面介绍的几种方式,在Spring环境下无需引入其他依赖就可以完成的,接下来要介绍的SnakeYml在使用前需要引入依赖,但是同时也可以脱离Spring环境单独使用。先引入依赖坐标:

<dependency><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId><version>1.23</version>
</dependency>

准备一个yml配置文件:

person1:name: hydragender: male
person2:name: fcantgender: female

在使用SnakeYml解析yml时,最常使用的就是loadloadlAllloadAs方法,这三个方法可以加载yml文件或字符串,最后返回解析后的对象。先从基础的load方法开始演示:

public void test1(){Yaml yaml=new Yaml();Map<String, Object> map =yaml.load(getClass().getClassLoader().getResourceAsStream("snake1.yml"));System.out.println(map);
}

运行上面的代码,打印Map中的内容:

{person1={name=hydra, gender=male}, person2={name=fcant, gender=female}}

接下来看一下loadAll方法,它可以用来加载yml中使用—-连接符连接的多个文档,将上面的yml文件进行修改:

person1:name: hydragender: male
---
person2:name: fcantgender: female

在添加了连接符后,尝试再使用load方法进行解析,报错显示发现了另一段yml文档从而无法正常解析。
这时候修改上面的代码,使用loadAll方法:

public void test2(){Yaml yaml=new Yaml();Iterable<Object> objects = yaml.loadAll(getClass().getClassLoader().getResourceAsStream("snake2.yml"));for (Object object : objects) {System.out.println(object);}
}

执行结果如下:

{person1={name=hydra, gender=male}}
{person2={name=fcant, gender=female}}

可以看到,loadAll方法返回的是一个对象的迭代,里面的每个对象对应yml中的一段文档,修改后的yml文件就被解析成了两个独立的Map。
接下来再来看一下loadAs方法,它可以在yml解析过程中指定类型,直接封装成一个对象。直接复用上面的snake1.yml,在解析前先创建两个实体类对象用于接收:

@Data
public class Person {SinglePerson person1;SinglePerson person2;
}
@Data
public class SinglePerson {String name;String gender;
}

下面使用loadAs方法加载yml,注意方法的第二个参数,就是用于封装yml的实体类型。

public void test3(){Yaml yaml=new Yaml();Person person = yaml.loadAs(getClass().getClassLoader().getResourceAsStream("snake1.yml"), Person.class);System.out.println(person.toString());
}

查看执行结果:

Person(person1=SinglePerson(name=hydra, gender=male), person2=SinglePerson(name=fcant, gender=female))

实际上,如果想要将yml封装成实体对象,也可以使用另一种方法。在创建Yaml对象的时候,传入一个指定实体类的构造器对象,然后直接调用load方法就可以实现:

public void test4(){Yaml yaml=new Yaml(new Constructor(Person.class));Person person = yaml.load(getClass().getClassLoader().getResourceAsStream("snake1.yml"));System.out.println(person.toString());
}

执行结果与上面相同:

Person(person1=SinglePerson(name=hydra, gender=male), person2=SinglePerson(name=fcant, gender=female))

SnakeYml其实实现了非常多的功能,这里就不一一列举了,有兴趣的小伙伴可以自己查看一下文档。其实在SpringBoot的底层,也是借助了SnakeYml来进行的yml的解析操作。 

5、jackson-dataformat-yaml

相比大家平常用jackson比较多的场景是用它来处理json,其实它也可以用来处理yml,使用前需要引入依赖:

<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-yaml</artifactId><version>2.12.3</version>
</dependency>

使用jackson读取yml也非常简单,这里用到了常用的ObjectMapper,在创建ObjectMapper对象时指定使用YAML工厂,之后就可以简单的将yml映射到实体:

public void read() throws IOException {ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());InputStream input =new FileInputStream("F:\\Work\\yml\\src\\main\\resources\\snake1.yml");Person person = objectMapper.readValue(input, Person.class);System.out.println(person.toString());
}

运行结果:

Person(person1=SinglePerson(name=hydra, gender=male), person2=SinglePerson(name=fcant, gender=female))

如果想要生成yml文件的话,可以调用ObjectMapperwriteValue方法实现:

public void write() throws IOException {Map<String,Object> map=new HashMap<>();SinglePerson person1 = new SinglePerson("Trunks", "male");SinglePerson person2 = new SinglePerson("Goten", "male");Person person=new Person(person1,person2);map.put("person",person);ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());objectMapper.writeValue(new File("F:\\Work\\yml\\src\\main\\resources\\jackson-gen.yml"),map);
}

查看生成的yml文件,可以看到jackson对字符串类型严格的添加了引号,还在文档的开头添加了yml的链接符。至于其他jackson读写yml的复杂功能,可以在工作中自己去探索使用。 

总结

这里介绍了5种读取yml配置文件的方式,前3种依赖于Spring环境,而SnakeYmlJackson则可以脱离环境独立使用,可以说它们是对@Value@ConfigurationProperties注解使用的补充。这几种方法的使用场景不同,也各有各的有优点,各自具备一些特殊的用法,而在工作中更多情况下,要根据具体的用途进行一种方案的选取或多种的搭配使用。

如果小假的内容对你有帮助,请点赞评论收藏。创作不易,大家的支持就是我坚持下去的动力!

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

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

相关文章

Spring Boot事务失效场景及解决方案

事务失效场景1&#xff1a;方法非public修饰 原因 Spring事务基于动态代理&#xff08;AOP&#xff09;实现&#xff0c;非public方法无法被代理拦截&#xff0c;导致事务失效。 代码示例 Service public class OrderService {Transactionalprivate void createOrder() { //…

电子电路:怎么理解时钟脉冲上升沿这句话?

时钟脉冲是数字电路中用于同步各组件操作的周期性信号&#xff0c;通常表现为高低电平交替的方波。理解其关键点如下&#xff1a; 时钟脉冲的本质&#xff1a; 由晶振等元件生成&#xff0c;呈现0/1&#xff08;低/高电平&#xff09;的规律振荡每个周期包含上升沿→高电平→下…

docker部署redis mysql nacos seata rabbitmq minio onlyoffice nginx实战

docker部署redis mysql nacos seata rabbitmq minio onlyoffice nginx实战 一、环境介绍 操作系统&#xff1a;ubuntu22.04 软件环境&#xff1a;docker、docker-compose 二、docker安装 版本规定到26.1.3版本过低会引起莫名其妙的问题。打开终端。更新软件包列表&#x…

全面解析:npm 命令、package.json 结构与 Vite 详解

全面解析&#xff1a;npm 命令、package.json 结构与 Vite 详解 一、npm run dev 和 npm run build 命令解析 1. npm run dev 作用&#xff1a;启动开发服务器&#xff0c;用于本地开发原理&#xff1a; 启动 Vite 开发服务器提供实时热更新&#xff08;HMR&#xff09;功能…

【Oracle】TCL语言

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. TCL概述1.1 什么是TCL&#xff1f;1.2 TCL的核心功能 2. 事务基础概念2.1 事务的ACID特性2.2 事务的生命周期 3. COMMIT语句详解3.1 COMMIT基础语法3.2 自动提交与手动提交3.3 提交性能优化 4. ROLLBACK语句…

OpenCV CUDA模块直方图计算------用于在 GPU 上执行对比度受限的自适应直方图均衡类cv::cuda::CLAHE

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::CLAHE 是 OpenCV 的 CUDA 模块中提供的一个类&#xff0c;用于在 GPU 上执行对比度受限的自适应直方图均衡&#xff08;Contrast Limi…

OpenGAN:基于开放数据生成的开放集识别

简介 简介&#xff1a;这次学习的OpenGAN主要学习一个思路&#xff0c;跳出传统GAN对于判断真假的识别到判断是已知种类还是未知种类。重点内容不在于代码而是思路&#xff0c;会简要给出一个设计的代码。 论文题目&#xff1a;OpenGAN: Open-Set Recognition via Open Data …

随机游动算法解决kSAT问题

input&#xff1a;n个变量的k-CNF公式 ouput&#xff1a;该公式的一组满足赋值或宣布没有满足赋值 算法步骤&#xff1a; 随机均匀地初始化赋值 a ∈ { 0 , 1 } n a\in\{0,1\}^n a∈{0,1}n.重复t次&#xff08;后面会估计这个t&#xff09;&#xff1a; a. 如果在当前赋值下…

企业上线ESOP电子作业指导书系统实现车间无纸化的投入收益数据综合分析

企业上线ESOP电子作业指导书系统实现车间无纸化的投入收益数据综合分析 一、成本节约&#xff1a;无纸化直接降低运营成本 纸张与耗材费用锐减 o 杭州科创致远案例&#xff1a;某汽配企业引入无纸化系统后&#xff0c;年节省纸张耗材费用超50万元。通过电子化替代传统纸质文档…

高并发抽奖系统优化方案

引子 最近接触了一个抽奖的项目&#xff0c;由于用户量比较大&#xff0c;而且第三方提供的认证接口并发量有限&#xff0c;为了保证服务的高可用性&#xff0c;所以对高并限制发有一定的要求。经过一系列研究和讨论&#xff0c;做出了以下一些优化方案。 需求分析 根据用户量…

STM32八股【10】-----stm32启动流程

启动流程 1.上电复位 2.系统初始化 3.跳转到 main 函数 启动入口&#xff1a; cpu被清空&#xff0c;程序从0x00000000开始运行0x00000000存放的是reset_handler的入口地址0x00000000的实际位置会变&#xff0c;根据不同的启动模式决定启动模式分为&#xff1a; flash启动&a…

LLMTIME: 不用微调!如何用大模型玩转时间序列预测?

今天是端午节&#xff0c;端午安康&#xff01;值此传统佳节之际&#xff0c;我想和大家分享一篇关于基于大语言模型的时序预测算法——LLMTIME。随着人工智能技术的飞速发展&#xff0c;利用大型预训练语言模型&#xff08;LLM&#xff09;进行时间序列预测成为一个新兴且极具…

在VirtualBox中打造高效开发环境:CentOS虚拟机安装与优化指南

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、为何选择VirtualBox CentOS组合&#xff1f; 对于程序员而言&#xff0c;构建隔离的开发测试环境是刚需。VirtualBox凭借其跨平台支持&#xff08;W…

LeeCode 98. 验证二叉搜索树

给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 提示&#xff1a; 树中节…

Python简易音乐播放器开发教程

&#x1f4da; 前言 编程基础第一期《12-30》–音乐播放器是日常生活中常用的应用程序&#xff0c;使用Python和pygame库可以轻松实现一个简易的音乐播放器。本教程将详细讲解如何开发一个具有基本功能的音乐播放器&#xff0c;并解析其中涉及的Python编程知识点。 &#x1f6e…

ssh连接断开,保持任务后台执行——tmux

目录 **核心用途****基础使用方法**1. **安装 tmux**2. **启动新会话**3. **常用快捷键&#xff08;需先按 Ctrlb 前缀&#xff09;**4. **会话管理命令**5. **窗格操作进阶** **典型工作流****注意事项****配置文件&#xff08;~/.tmux.conf&#xff09;** tmux&#xff08; …

3D Gaussian splatting 04: 代码阅读-提取相机位姿和稀疏点云

目录 3D Gaussian splatting 01: 环境搭建3D Gaussian splatting 02: 快速评估3D Gaussian splatting 03: 用户数据训练和结果查看3D Gaussian splatting 04: 代码阅读-提取相机位姿和稀疏点云3D Gaussian splatting 05: 代码阅读-训练整体流程3D Gaussian splatting 06: 代码…

每日c/c++题 备战蓝桥杯(P1204 [USACO1.2] 挤牛奶 Milking Cows)

P1204 [USACO1.2] 挤牛奶 Milking Cows - 详解与代码实现 一、题目背景 三个农民每天清晨[……]&#xff08;简要介绍题目背景&#xff0c;与官网描述类似&#xff09; 二、问题分析 输入要求 &#xff1a;读取 N 个农民的挤奶时间区间&#xff0c;计算两个值&#xff1a;最…

保持本地 Git 项目副本与远程仓库完全同步

核心目标&#xff1a; 保持本地 Git 项目副本与 GitHub 远程仓库完全同步。 关键方法&#xff1a; 定期执行 git pull 命令。 操作步骤&#xff1a; 进入项目目录&#xff1a; 在终端/命令行中&#xff0c;使用 cd 命令切换到你的项目文件夹。执行拉取命令&#xff1a; 运行…

Flutter 4.x 版本 webview_flutter 嵌套H5

踩坑早期版本 使用 WebView 代码如下 import package:flutter/material.dart; import package:webview_flutter/webview_flutter.dart;class HomePage extends StatelessWidget {const HomePage({super.key});overrideWidget build(BuildContext context) {return Scaffold(ap…