Original: WeChat official account, welcome to share, reprint, please keep the source.
In the previous article, we analyzed the whole process of SpringBoot parsing yml configuration files from the perspective of source code. Today, let's talk about the actual combat and summarize the ways to read the contents of yml configuration files in addition to @ Value and @ ConfigurationProperties.
1,Environment
There is a class Environment in Spring, which can be considered as the running Environment of the current application. It inherits the PropertyResolver interface, so it can be used as a property parser. First create a yml file with the following attributes:
person: name: hydra gender: male age: 18
It is also very simple to use. It can be injected directly into the class to be used directly with @Autowired, and then its getProperty() method can be used to extract the corresponding value according to the property name.
@RestController public class EnvironmentController { @Autowired private 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); } }
As can be seen from the above example, in addition to simple acquisition, the methods provided by the Environment can also type convert the retrieved attribute values and set the default values. Call the above interface, and the print results are as follows:
hydra male 18 defaultValue
In addition to obtaining properties, it can also be used to judge the activated configuration file. Let's start with application Activate pro file in YML:
spring: profiles: active: pro
You can use the acceptsProfiles method to detect whether a configuration file is activated and loaded, or get all activated configuration files through the getActiveProfiles method. Test interface:
@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); } }
Print results:
true false pro
2,YamlPropertiesFactoryBean
In Spring, you can also use YamlPropertiesFactoryBean to read the yml file of custom configuration, instead of being bound to application yml and other profiles it activates.
During use, you only need to set the storage path of the custom yml configuration file through the setResources() method, and then obtain the Properties object through the getObject() method. Later, you can obtain specific Properties through it. See an example below:
@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()); }
Viewing the running results, you can read the specified application 2 YML content:
susan female {person2.age=18, person2.gender=female, person2.name=susan}
However, there is a problem in such use, that is, the value of this property can only be obtained in the request of this interface. If another interface is written, the configuration file is not read by YamlPropertiesFactoryBean. Even if the previous method has read the yml file once, the value obtained by the second interface is still null. To test this process:
@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); }
When the fcTest interface is called first and then the fcTest2 interface is called, the null value will be printed:
null null
It is also very simple to solve this problem. It can be used in conjunction with PropertySourcesPlaceholderConfigurer, which implements the beanfactoryprocessor interface, that is, the implementation of a bean factory postprocessor, and can load the property values of the configuration file into a Properties file. The method of use is as follows:
@Configuration public class PropertyConfig { @Bean public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); YamlPropertiesFactoryBean yamlProFb = new YamlPropertiesFactoryBean(); yamlProFb.setResources(new ClassPathResource("application2.yml")); configurer.setProperties(yamlProFb.getObject()); return configurer; } }
Call the previous interface again. The results are as follows. You can get application2 normally Attributes in YML:
susan female
In addition to using YamlPropertiesFactoryBean to parse yml into Properties, we can also use YamlMapFactoryBean to parse yml into Map in a very similar way:
@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); }
Print results:
{person2={name=susan, gender=female, age=18}}
3. Listening events
In the previous article on the principle, we know that SpringBoot loads and parses yml files by listening to events, so we can also follow this pattern to load custom configuration files.
First, define a class to implement the ApplicationListener interface, listen to the event type ApplicationEnvironmentPreparedEvent, and pass in the yml file name to be resolved in the constructor:
public class YmlListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> { private String ymlFilePath; public YmlListener(String ymlFilePath){ this.ymlFilePath = ymlFilePath; } //... }
The onApplicationEvent() method of the interface needs to be implemented in the custom listener. It will be triggered when the ApplicationEnvironmentPreparedEvent event is heard:
@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(); } }
The above code mainly implements:
- Obtain the current Environment. When the ApplicationEnvironmentPreparedEvent event is triggered, the Environment has been loaded and can be obtained through the event event
- Load and parse configuration files through YamlPropertySourceLoader
- Add the origintrackedmapppropertysource after parsing to the Environment
Modify the startup class and 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); }
Add a breakpoint before adding propertySource to the environment to check the changes of the environment:
After execution, you can see that the configuration file source has been added to the environment:
After startup, call the interface again to view the results:
susan female
The value in the configuration file can be obtained correctly, indicating that the customized listener has taken effect.
4,SnakeYml
The above methods can be completed without introducing other dependencies in the Spring environment. The SnakeYml to be introduced next needs to introduce dependencies before use, but it can also be used separately from the Spring environment. Import dependent coordinates first:
<dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.23</version> </dependency>
Prepare a yml profile:
person1: name: hydra gender: male person2: name: susan gender: female
When using snake yml to parse yml, the most commonly used methods are load, LOADALL and loadAs. These three methods can load yml files or strings, and finally return the parsed objects. Let's start with the basic load method:
public void test1(){ Yaml yaml=new Yaml(); Map<String, Object> map = yaml.load(getClass().getClassLoader() .getResourceAsStream("snake1.yml")); System.out.println(map); }
Run the above code to print the contents in the Map:
{person1={name=hydra, gender=male}, person2={name=susan, gender=female}}
Next, let's take a look at the loadAll method, which can be used to load multiple documents connected with --- connectors in yml and modify the above yml files:
person1: name: hydra gender: male --- person2: name: susan gender: female
After adding the connector, try to use the load method for parsing. The error is as follows: another yml document is found and cannot be parsed normally:
At this time, modify the above code and use the loadAll method:
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); } }
The results are as follows:
{person1={name=hydra, gender=male}} {person2={name=susan, gender=female}}
You can see that the loadAll method returns an iteration of an object. Each object in the object corresponds to a document in the yml. The modified yml file is parsed into two independent maps.
Next, let's take a look at the loadAs method, which can specify the type in the yml parsing process and directly encapsulate it into an object. We directly reuse the above snake 1 yml, before parsing, create two entity class objects to receive:
@Data public class Person { SinglePerson person1; SinglePerson person2; } @Data public class SinglePerson { String name; String gender; }
Next, use the loadAs method to load the yml. Note that the second parameter of the method is the entity type used to encapsulate the yml.
public void test3(){ Yaml yaml=new Yaml(); Person person = yaml.loadAs(getClass().getClassLoader(). getResourceAsStream("snake1.yml"), Person.class); System.out.println(person.toString()); }
View execution results:
Person(person1=SinglePerson(name=hydra, gender=male), person2=SinglePerson(name=susan, gender=female))
In fact, if you want to encapsulate yml as an entity object, you can use another method. When creating a Yaml object, pass in a constructor object of the specified entity class, and then directly call the load method to achieve:
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()); }
The execution result is the same as above:
Person(person1=SinglePerson(name=hydra, gender=male), person2=SinglePerson(name=susan, gender=female))
SnakeYml actually realizes many functions. I won't list them one by one here. Interested partners can check the documents themselves. If you read the source code after reading the last article, you will find that in fact, at the bottom of SpringBoot, you also use SnakeYml to parse yml.
5,jackson-dataformat-yaml
jackson is used to handle json more often than others. In fact, it can also be used to handle yml. Before using it, you need to introduce dependencies:
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> <version>2.12.3</version> </dependency>
It is also very easy to read yml using jackson. The common ObjectMapper is used here. Specify YAML factory when creating ObjectMapper object, and then simply map yml to entity:
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()); }
Operation results:
Person(person1=SinglePerson(name=hydra, gender=male), person2=SinglePerson(name=susan, gender=female))
If you want to generate yml files, you can call the writeValue method of ObjectMapper to implement:
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); }
Looking at the generated yml file, you can see that jackson strictly added quotation marks to the string type and added yml link characters at the beginning of the document. As for other complex functions of jackson reading and writing yml, you can explore and use them yourself in your work.
summary
This article introduces five ways to read yml configuration files. The first three depend on the Spring environment, while snake yml and Jackson can be used independently of the environment. It can be said that they are complementary to the use of @ Value and @ ConfigurationProperties annotations. The usage scenarios of these methods are different, and each has its own advantages, and each has some special usage. In our work, we should select one scheme or use multiple combinations according to the specific purpose.
Well, I hope this actual battle can help you. I'm Hydra. See you next time.
The author introduces a public official account for code sharing, which is interesting, deep and direct, and talks with you about technology. Personal wechat DrHydra9, welcome to add friends for further communication.