1、Environment
在Spring中有一个类Environment,它能够被认为是以后应用程序正在运行的环境,它继承了PropertyResolver接口,因而能够作为一个属性解析器应用。先创立一个yml文件,属性如下:
person:
name: hydra
gender: male
age: 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的内容:
susan
female
{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值:
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中的属性:
susan
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=susan, 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前加一个断点,查看环境的变动:

执行实现后,能够看到配置文件源曾经被增加到了环境中:

启动实现后再调用一下接口,查看后果:
susan
female
复制代码
可能正确的取到配置文件中的值,阐明自定义的监听器曾经失效。
4、SnakeYml
后面介绍的几种形式,在Spring环境下无需引入其余依赖就能够实现的,接下来要介绍的SnakeYml在应用前须要引入依赖,然而同时也能够脱离Spring环境独自应用。先引入依赖坐标:
<dependency>

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

</dependency>
复制代码
筹备一个yml配置文件:
person1:
name: hydra
gender: male
person2:
name: susan
gender: 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: hydra

gender: male

person2:
name: susan
gender: 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,在解析前先创立两个实体类对象用于接管:
@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=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注解应用的补充。这几种办法的应用场景不同,也各有各的有长处,各自具备一些非凡的用法,而咱们在工作中更多状况下,要依据具体的用处进行一种计划的选取或多种的搭配应用。