【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
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/83126.shtml
英文地址,請注明出處:http://en.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…