
文章插图
只会用@Value和@ConfigurationProperties?那么来看看另外5种方法吧~原创:微信公众号
码农参上 , 欢迎分享 , 转载请保留出处 。在上一篇文章中 , 我们从源码角度分析了SpringBoot解析yml配置文件的全流程 , 那么我们今天就来点实战 , 总结一下除了烂大街的
@Value和@ConfigurationProperties外 , 还能够通过哪些方式 , 来读取yml配置文件的内容 。1、Environment在Spring中有一个类
Environment , 它可以被认为是当前应用程序正在运行的环境 , 它继承了PropertyResolver接口 , 因此可以作为一个属性解析器使用 。先创建一个yml文件 , 属性如下:person:name: hydragender: maleage: 18使用起来也非常简单 , 直接使用@Autowired就可以注入到要使用的类中 , 然后调用它的getProperty()方法就可以根据属性名称取出对应的值了 。@RestControllerpublic 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 = https://tazarkount.com/read/environment.getProperty("person.other", String.class, "defaultValue");System.out.println(defaultValue);}}在上面的例子中可以看到 , 除了简单的获取外 , Environment提供的方法还可以对取出的属性值进行类型转换、以及默认值的设置 , 调用一下上面的接口 , 打印结果如下:hydramale18defaultValue除了获取属性外 , 还可以用来判断激活的配置文件 , 我们先在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);}}打印结果:truefalsepro2、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的内容:susanfemale{person2.age=18, person2.gender=female, person2.name=susan}但是这样的使用中有一个问题 , 那就是只有在这个接口的请求中能够取到这个属性的值 , 如果再写一个接口 , 不使用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值:nullnull想要解决这个问题也很简单 , 可以配合PropertySourcesPlaceholderConfigurer使用 , 它实现了BeanFactoryPostProcessor接口 , 也就是一个bean工厂后置处理器的实现 , 可以将配置文件的属性值加载到一个Properties文件中 。使用方法如下:@Configurationpublic 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中的属性:susanfemale除了使用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=susan, gender=female, age=18}}3、监听事件在上篇介绍原理的文章中 , 我们知道SpringBoot是通过监听事件的方式来加载和解析的yml文件 , 那么我们也可以仿照这个模式 , 来加载自定义的配置文件 。首先 , 定义一个类实现
ApplicationListener接口 , 监听的事件类型为ApplicationEnvironmentPreparedEvent , 并在构造方法中传入要解析的yml文件名:public class YmlListener implementsApplicationListener<ApplicationEnvironmentPreparedEvent> {private String ymlFilePath;public YmlListener(String ymlFilePath){this.ymlFilePath = ymlFilePath;}//...}【高大上 5种高大上的yml文件读取方式,你知道吗?】自定义的监听器中需要实现接口的onApplicationEvent()方法 , 当监听到ApplicationEnvironmentPreparedEvent事件时会被触发:@Overridepublic 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前加一个断点 , 查看环境的变化:
文章插图
执行完成后 , 可以看到配置文件源已经被添加到了环境中:

文章插图
启动完成后再调用一下接口 , 查看结果:
susanfemale能够正确的取到配置文件中的值 , 说明自定义的监听器已经生效 。4、SnakeYml前面介绍的几种方式 , 在Spring环境下无需引入其他依赖就可以完成的 , 接下来要介绍的
SnakeYml在使用前需要引入依赖 , 但是同时也可以脱离Spring环境单独使用 。先引入依赖坐标:<dependency><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId><version>1.23</version></dependency>准备一个yml配置文件:person1:name: hydragender: maleperson2:name: susangender: female在使用SnakeYml解析yml时 , 最常使用的就是load、loadlAll、loadAs方法 , 这三个方法可以加载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=susan, gender=female}}接下来看一下loadAll方法 , 它可以用来加载yml中使用---连接符连接的多个文档 , 将上面的yml文件进行修改:person1:name: hydragender: male---person2:name: susangender: 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=susan, gender=female}}可以看到 , loadAll方法返回的是一个对象的迭代 , 里面的每个对象对应yml中的一段文档 , 修改后的yml文件就被解析成了两个独立的Map 。接下来再来看一下
loadAs方法 , 它可以在yml解析过程中指定类型 , 直接封装成一个对象 。我们直接复用上面的snake1.yml , 在解析前先创建两个实体类对象用于接收:@Datapublic class Person {SinglePerson person1;SinglePerson person2;}@Datapublic 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=susan, 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=susan, 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=susan, gender=female))如果想要生成yml文件的话 , 可以调用ObjectMapper的writeValue方法实现: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环境 , 而
SnakeYml和Jackson则可以脱离环境独立使用 , 可以说它们是对@Value和@ConfigurationProperties注解使用的补充 。这几种方法的使用场景不同 , 也各有各的优点 , 各自具备一些特殊的用法 , 而我们在工作中更多情况下 , 要根据具体的用途进行一种方案的选取或多种的搭配使用 。好了 , 希望这篇实战能够帮助到大家 , 我是Hydra , 我们下篇再见 。
作者简介 , 码农参上 , 一个热爱分享的公众号 , 有趣、深入、直接 , 与你聊聊技术 。个人微信DrHydra9 , 欢迎添加好友 , 进一步交流 。
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
