mall学习教程官网:macrozheng.com

上一篇文章中,我们从源码角度分析了SpringBoot解析yml配置文件的全流程,那么我们今天就来点实战,总结一下除了烂大街的@Value@ConfigurationProperties外,还能够通过哪些方式,来读取yml配置文件的内容。

1、Environment

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

person:name:hydragender:maleage:18

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

@RestControllerpublicclassEnvironmentController{@AutowiredprivateEnvironmentenvironment;@GetMapping("envTest")privatevoidgetEnv(){System.out.println(environment.getProperty("person.name"));System.out.println(environment.getProperty("person.gender"));IntegerautoClose=environment.getProperty("person.age",Integer.class);System.out.println(autoClose);StringdefaultValue=environment.getProperty("person.other",String.class,"defaultValue");System.out.println(defaultValue);}}

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

hydramale18defaultValue

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

spring:profiles:active:pro

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

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

打印结果:

truefalsepro

这或许是一个对你有用的开源项目,mall项目是一套基于 SpringBoot3 + JDK 17 + Vue 实现的电商系统(Github标星60K),采用Docker容器化部署,后端支持多模块和微服务架构。包括前台商城项目和后台管理系统,能支持完整的订单流程!涵盖商品、订单、购物车、权限、优惠券、会员、支付等功能!

  • Boot项目:https://github.com/macrozheng/mall
  • Cloud项目:https://github.com/macrozheng/mall-swarm
  • 视频教程:https://www.macrozheng.com/video/

项目演示:

2、YamlPropertiesFactoryBean

在Spring中还可以使用YamlPropertiesFactoryBean来读取自定义配置的yml文件,而不用再被拘束于application.yml及其激活的其他配置文件。

在使用过程中,只需要通过setResources()方法设置自定义yml配置文件的存储路径,再通过getObject()方法获取Properties对象,后续就可以通过它获取具体的属性,下面看一个例子:

@GetMapping("fcTest")publicvoidymlProFctest(){YamlPropertiesFactoryBeanyamlProFb=newYamlPropertiesFactoryBean();yamlProFb.setResources(newClassPathResource("application2.yml"));Propertiesproperties=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}")privateStringname;@Value("${person2.gender:null}")privateStringgender;@GetMapping("fcTest2")publicvoidymlProFctest2(){System.out.println(name);System.out.println(gender);}

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

nullnull

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

@ConfigurationpublicclassPropertyConfig{@BeanpublicstaticPropertySourcesPlaceholderConfigurerplaceholderConfigurer(){PropertySourcesPlaceholderConfigurerconfigurer=newPropertySourcesPlaceholderConfigurer();YamlPropertiesFactoryBeanyamlProFb=newYamlPropertiesFactoryBean();yamlProFb.setResources(newClassPathResource("application2.yml"));configurer.setProperties(yamlProFb.getObject());returnconfigurer;}}

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

susanfemale

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

@GetMapping("fcMapTest")publicvoidymlMapFctest(){YamlMapFactoryBeanyamlMapFb=newYamlMapFactoryBean();yamlMapFb.setResources(newClassPathResource("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文件名:

publicclassYmlListenerimplementsApplicationListener<ApplicationEnvironmentPreparedEvent>{privateStringymlFilePath;publicYmlListener(StringymlFilePath){this.ymlFilePath=ymlFilePath;}//...}

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

@OverridepublicvoidonApplicationEvent(ApplicationEnvironmentPreparedEventevent){ConfigurableEnvironmentenvironment=event.getEnvironment();ResourceLoaderloader=newDefaultResourceLoader();YamlPropertySourceLoaderymlLoader=newYamlPropertySourceLoader();try{List<PropertySource<?>>sourceList=ymlLoader.load(ymlFilePath,loader.getResource(ymlFilePath));for(PropertySource<?>propertySource:sourceList){environment.getPropertySources().addLast(propertySource);}}catch(IOExceptione){e.printStackTrace();}}

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

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

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

publicstaticvoidmain(String[]args){SpringApplicationapplication=newSpringApplication(MyApplication.class);application.addListeners(newYmlListener("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时,最常使用的就是loadloadlAllloadAs方法,这三个方法可以加载yml文件或字符串,最后返回解析后的对象。我们先从基础的load方法开始演示:

publicvoidtest1(){Yamlyaml=newYaml();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方法:

publicvoidtest2(){Yamlyaml=newYaml();Iterable<Object>objects=yaml.loadAll(getClass().getClassLoader().getResourceAsStream("snake2.yml"));for(Objectobject:objects){System.out.println(object);}}

执行结果如下:

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

可以看到,loadAll方法返回的是一个对象的迭代,里面的每个对象对应yml中的一段文档,修改后的yml文件就被解析成了两个独立的Map。

接下来再来看一下loadAs方法,它可以在yml解析过程中指定类型,直接封装成一个对象。我们直接复用上面的snake1.yml,在解析前先创建两个实体类对象用于接收:

@DatapublicclassPerson{SinglePersonperson1;SinglePersonperson2;}@DatapublicclassSinglePerson{Stringname;Stringgender;}

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

publicvoidtest3(){Yamlyaml=newYaml();Personperson=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方法就可以实现:

publicvoidtest4(){Yamlyaml=newYaml(newConstructor(Person.class));Personperson=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映射到实体:

publicvoidread()throwsIOException{ObjectMapperobjectMapper=newObjectMapper(newYAMLFactory());InputStreaminput=newFileInputStream("F:\Work\yml\src\main\resources\snake1.yml");Personperson=objectMapper.readValue(input,Person.class);System.out.println(person.toString());}

运行结果:

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

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

publicvoidwrite()throwsIOException{Map<String,Object>map=newHashMap<>();SinglePersonperson1=newSinglePerson("Trunks","male");SinglePersonperson2=newSinglePerson("Goten","male");Personperson=newPerson(person1,person2);map.put("person",person);ObjectMapperobjectMapper=newObjectMapper(newYAMLFactory());objectMapper.writeValue(newFile("F:\Work\yml\src\main\resources\jackson-gen.yml"),map);}

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

总结

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


Github上标星60K的电商实战项目mall,全套 视频教程(2023最新版) 已更新完毕!全套教程约40小时,共113期,通过这套教程你可以拥有一个涵盖主流Java技术栈的完整项目经验,同时提高自己独立开发一个项目的能力,下面是项目的整体架构图,感兴趣的小伙伴可以点击链接 mall视频教程 加入学习。

整套 视频教程 的内容还是非常完善的,涵盖了mall项目最佳学习路线、整体框架搭建、业务与技术实现全方位解析、线上Docker环境部署、微服务项目学习等内容,你也可以点击链接 mall视频教程 了解更多内容。

推荐阅读

本篇文章来源于微信公众号: macrozheng



微信扫描下方的二维码阅读本文

此作者没有提供个人介绍
最后更新于 2024-08-01