In this post, we will see how to read values from the YAML file and inject them into the java model class.
YAML is a human-readable language used for configurations also the yaml solves hierarchical configuration where we can define complex structures.
If you don't know how to create a spring boot project, see the Create Spring Boot Application.
Spring boot Configuration Properties
---
employee:
firstname: bharath
lastname: kumar
age: 25
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import lombok.Data;
@Data
@Component
@ConfigurationProperties(prefix = "employee")
@EnableConfigurationProperties
public class Employee {
private String firstname;
private String lastname;
private int age;
}
Loading external custom YAML file
import java.io.IOException;
import java.util.Arrays;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
@Configuration
public class YAMLConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer yamlproperties() {
PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer();
YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
Resource[] yamlfiles = null;
try {
yamlfiles = patternResolver.getResources("classpath*:/*.yaml");
System.out.println(Arrays.toString(yamlfiles));
} catch (IOException e) {
e.printStackTrace();
}
factoryBean.setResources(yamlfiles);
placeholderConfigurer.setProperties(factoryBean.getObject());
return placeholderConfigurer;
}
}
Inject map from YAML
---
employee:
firstname: bharath
lastname: kumar
age: 25
phonenumbers:
office: 0442345555
home: 911233333333
personal: 9840002222
Inject list from YAML
---
employee:
firstname: bharath
lastname: kumar
age: 25
phonenumbers:
office: 0442345555
home: 911233333333
personal: 9840002222
skils:
- java
- spring
- hibernate
- aws
Inject nested properties from YAML
---
employee:
firstname: bharath
lastname: kumar
age: 25
address:
street: dhanasekran
city: vice city
state: GTA
postalcode: 600024
phonenumbers:
office: 0442345555
home: 911233333333
personal: 9840002222
skils:
- java
- spring
- hibernate
- aws
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import lombok.Data;
@Data
@Component
@ConfigurationProperties(prefix = "employee")
@EnableConfigurationProperties
public class Employee {
private String firstname;
private String lastname;
private int age;
private Address address;
private Map<String, String> phonenumbers;
private List<String> skils;
@Data
static class Address {
private String street;
private String city;
private String state;
private long postalcode;
}
}
Testing
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.example.demo.config.Employee;
@SpringBootTest
class EmployeeTest {
@Autowired
Employee employee;
@Test
void checkTheYamlInjection() {
System.out.println(employee.toString());
assertEquals("bharath", employee.getFirstname());
assertEquals("kumar", employee.getLastname());
assertNotNull(employee.getAddress());
}
}
Constructor binding
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import lombok.AllArgsConstructor;
import lombok.ToString;
@AllArgsConstructor
@ConstructorBinding
@ConfigurationProperties(prefix = "employee")
@ToString
public class Employee {
private String firstname;
private String lastname;
private int age;
private Address address;
private Map<String, String> phonenumbers;
private List<String> skils;
@AllArgsConstructor
@ToString
@ConstructorBinding
static class Address {
private String street;
private String city;
private String state;
private long postalcode;
}
}
If we have more than one constructor in class then we can place the annotation on the constructor which we want to use for binding values.
we have to add @ConfigurationPropertiesScan annotation to our main spring boot application class to enable support for configuration properties and the bean gets created for the class annotated with @ConfigurationProperties.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan
public class SpringBootTutorialApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootTutorialApplication.class, args);
}
}
Validation
Let's add bean validation to our class to ensure that values are injected and values are validated purposely. It's always better to add validation to configuration properties, so we find the possible configuration error earlier.
Add @Validated to our employee.class
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.validation.annotation.Validated;
import lombok.AllArgsConstructor;
import lombok.ToString;
@AllArgsConstructor
@ConstructorBinding
@ConfigurationProperties(prefix = "employee")
@ToString
@Validated
public class Employee {
@NotBlank(message = "Firstname cannot be blank")
private String firstname;
@NotBlank(message = "Lastname cannot be blank")
private String lastname;
private int age;
@NotNull(message = "Address must be present")
private Address address;
private Map < String, String > phonenumbers;
private List < String > skils;
@AllArgsConstructor
@ToString
@ConstructorBinding
static class Address {
private String street;
private String city;
private String state;
private long postalcode;
}
}
If we remove employee firstname then we will get the below error on application startup.