“This is the second day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.


In the last article, we analyzed the whole process of parsing YML configuration files by SpringBoot from the source point of view, so today we will have a little bit of practice, and we will have a little bit of practice. Besides @Value and @configurationProperties, we will have a little bit of practice. To read the contents of the YML configuration file.

1, the Environment,

There is one class in Spring, Environment, which can be thought of as the Environment in which the current application is running, inherits the PropertyResolver interface and can therefore be used as a PropertyResolver. Create a yML file with the following properties:

person:
  name: hydra
  gender: male
  age: 18
Copy the code

It’s also very simple to use. You can inject it into the class you want to use using @AutoWired, and then call its getProperty() method to fetch the value based on the property name.

@RestController
public class EnvironmentController {
    @Autowired
    private Environment environment;

    @GetMapping("envTest")
    private void getEnv(a){
        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); }}Copy the code

As you can see in the above example, the method provided by Environment can also cast the value of the fetched attribute and set the default value. Call the above interface and print the result as follows:

hydra
male
18
defaultValue
Copy the code

In addition to obtaining properties, it can also be used to determine the active configuration file. We first activate the pro file in application.

spring:
  profiles:
    active: pro
Copy the code

You can use the acceptsProfiles method to check if a profile is active or loaded, or the getActiveProfiles method to get all active profiles. Test interface:

@GetMapping("getActiveEnv")
private void getActiveEnv(a){
    System.out.println(environment.acceptsProfiles("pro"));
    System.out.println(environment.acceptsProfiles("dev"));

    String[] activeProfiles = environment.getActiveProfiles();
    for(String activeProfile : activeProfiles) { System.out.println(activeProfile); }}Copy the code

Print result:

true
false
pro
Copy the code

2, YamlPropertiesFactoryBean

In Spring can also use YamlPropertiesFactoryBean yml file to read from the definition of configuration, and don’t have to be limited in application, yml and activate other configuration files.

Use the setResources() method to set the path to the custom YML configuration file. Use the getObject() method to get the Properties object, and then use it to get the specific Properties. Here’s an example:

@GetMapping("fcTest")
public void ymlProFctest(a){
    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());
}
Copy the code

To view the results, you can read the contents of the specified application2.yml:

susan
female
{person2.age=18, person2.gender=female, person2.name=susan}
Copy the code

But in the use of such a problem, it is only in the interface can take to the value of this attribute in the request, if you write an interface that does not use YamlPropertiesFactoryBean reading configuration files, even before the methods have been read the yml file again, The second interface still fetches a null value. To test this process:

@Value("${person2.name:null}")
private String name;
@Value("${person2.gender:null}")
private String gender;

@GetMapping("fcTest2")
public void ymlProFctest2(a){
    System.out.println(name);
    System.out.println(gender);
}
Copy the code

Null is printed when fcTest is called once and fcTest2 is called again:

null
null
Copy the code

Want to solve this problem is simple, can be used with PropertySourcesPlaceholderConfigurer, it implements the spring BeanFactoryPostProcessor interface, which is a bean factory rear processor implementation, You can load the property values of the configuration file into a Properties file. The usage method is as follows:

@Configuration
public class PropertyConfig {
    @Bean
    public static PropertySourcesPlaceholderConfigurer placeholderConfigurer(a) {
        PropertySourcesPlaceholderConfigurer configurer 
            = new PropertySourcesPlaceholderConfigurer();
        YamlPropertiesFactoryBean yamlProFb 
            = new YamlPropertiesFactoryBean();
        yamlProFb.setResources(new ClassPathResource("application2.yml"));
        configurer.setProperties(yamlProFb.getObject());
        returnconfigurer; }}Copy the code

Call the interface again, the result is as follows, and the property in application2.yml can be retrieved normally:

susan
female
Copy the code

In addition to using YamlPropertiesFactoryBean yml parsed into the Properties, in fact, we can also use the analytical yml YamlMapFactoryBean become Map, using methods very similar:

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

Print result:

{person2={name=susan, gender=female, age=18}}
Copy the code

3. Listening events

In the previous principle article, we learned that SpringBoot is a YML file that listens for events to load and parse, so we can also use this pattern to load custom configuration files.

First, define a class implements ApplicationListener interface, to monitor the event type as ApplicationEnvironmentPreparedEvent, and passed in the constructor to parse yml file name:

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

Custom listener needs to implement the interface of onApplicationEvent () method, when listening to ApplicationEnvironmentPreparedEvent event will be triggered:

@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(); }}Copy the code

In the above code, the main implementation is:

  • Get the current environmentEnvironmentwhenApplicationEnvironmentPreparedEventWhen the event is triggered, it is doneEnvironmentAnd can pass througheventEvents for
  • throughYamlPropertySourceLoaderLoad and parse the configuration file
  • Will parse the finishedOriginTrackedMapPropertySourceAdded to theEnvironmentIn the

Add this listener to the startup class:

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

Add a breakpoint before adding the propertySource to the environment to see the changes to the environment:

After executing, you can see that the configuration file source has been added to the environment:

After the startup is complete, call the interface again and check the result:

susan
female
Copy the code

If the value in the configuration file can be correctly obtained, the customized listener has taken effect.

4, SnakeYml

SnakeYml requires the introduction of dependencies before use, but can also be used independently of the Spring environment. First introduce dependent coordinates:

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

Prepare a YML configuration file:

person1:
  name: hydra
  gender: male
person2:
  name: susan
  gender: female
Copy the code

When parsing YML using SnakeYml, the most commonly used methods are load, loadlAll, and loadAs, which load YML files or strings and return parsed objects. Let’s start with the basic load method:

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

Run the code above to print the contents of the Map:

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

Let’s take a look at the loadAll method, which can be used to load multiple documents in YML that are concatenated with the — connecter, and modify the above yML file:

person1:
  name: hydra
  gender: male
---
person2:
  name: susan
  gender: female
Copy the code

After adding the concatenation, try to use the load method to parse again. Error: another YML document is found and cannot be parsed normally:

Modify the above code to use the loadAll method:

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

The result is as follows:

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

As you can see, the loadAll method returns an iteration of an object, each of which corresponds to a document in YML. The modified YML file is parsed into two separate maps.

Next, take a look at the loadAs method, which can specify a type during YML parsing and encapsulate it directly into an object. We directly reuse snake1.yml above, creating two entity-class objects to receive before parsing:

@Data
public class Person {
    SinglePerson person1;
    SinglePerson person2;
}

@Data
public class SinglePerson {
    String name;
    String gender;
}
Copy the code

Now load yML using the loadAs method. Note that the second argument to the method is the entity type used to encapsulate yML.

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

View the execution result:

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

In fact, if you want to encapsulate YML as an entity object, you can use another approach. When creating a Yaml object, pass in a constructor object that specifies the entity class and call the load method directly:

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

The result is the same as above:

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

SnakeYml actually implements a lot of functions, here is not a list, interested partners can check the documentation. If you read the previous article followed by a look at the source code, then you will find that in fact, at the bottom of SpringBoot, also with the help of SnakeYml to carry out yML parsing operations.

5, Jackson – dataformat – yaml

It can also be used to work with YML, and requires dependencies before using it:

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

Using Jackson to read yML is also very simple, using the common ObjectMapper. Specify YAML factory when creating ObjectMapper objects, then you can simply map YML to entities:

public void read(a) 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());
}
Copy the code

Running results:

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

If you want to generate yML files, you can call ObjectMapper’s writeValue method:

public void write(a) 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);
}
Copy the code

Looking at the generated YML file, you can see that Jackson quotes the string type strictly and adds a YML link at the beginning of the document. As for Jackson’s other complex features of reading and writing YML, you can explore them yourself at work.

conclusion

This article describes five ways to read YML configuration files. The first three depend on the Spring environment, while SnakeYml and Jackson can be used independently of the environment, complementing the use of the @Value and @ConfigurationProperties annotations. These methods have different use scenarios, each has its own advantages, each has some special usage, and we in the work of more cases, according to the specific use of a scheme to choose or a variety of collocation.

Well, I hope this article was helpful. I’m Hydra and I’ll see you next time.

If you missed the previous principle article, check this out:

18 pictures to explain the whole process of Parsing YML by SpringBoot

The last

If you think it’s helpful, you can like it. Thank you very much

Nongcanshang, an interesting, in-depth and direct public account that loves sharing, will talk to you about technology. Add a friend and make a “like” friend