1 2 3 4 5 6 7 8 9 10 | prefix: stringProp1: propValue1 stringProp2: propValue2 intProp1: 10 listProp: - listValue1 - listValue2 mapProp: key1: mapValue1 key2: mapValue2 |
These entries can also be described in a traditional application.properties file the following way:
1 2 3 4 5 6 7 | prefix.stringProp1=propValue1 prefix.stringProp2=propValue2 prefix.intProp1= 10 prefix.listProp[ 0 ]=listValue1 prefix.listProp[ 1 ]=listValue2 prefix.mapProp.key1=mapValue1 prefix.mapProp.key2=mapValue2 |
It has taken me a little while, but I do like the hierarchical look of the properties described in a yaml format.
So now, given this property file a traditional Spring application would have loaded up the properties the following way:
1 2 3 | public class SamplePropertyLoadingTest { @Value ( "${prefix.stringProp1}" ) private String stringProp1; |
Note the placeholder for "prefix.stringProp" key.
This however is not ideal for loading a family of related properties, say in this specific case namespaced by the prefix conveniently named "prefix".
The approach Spring boot takes is to define a bean that can hold all the family of related properties this way:
1 2 3 4 5 6 7 8 9 10 11 12 13 | @ConfigurationProperties (prefix = "prefix" ) @Component public class SampleProperty { private String stringProp1; private String stringProp2; @Max ( 99 ) @Min ( 0 ) private Integer intProp1; private List<String> listProp; private Map<String, String> mapProp; ... } |
At runtime, all the fields would be bound to the related properties cleanly.
Additionally note the JSR-303 annotations on top of the "intProp1" field that validates that value of the field is between 0 and 99, @ConfigurationProperties will call the validator to ensure that bound bean is validated.
An integration test making use of this is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package prop; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; @RunWith (SpringJUnit4ClassRunner. class ) @SpringApplicationConfiguration (classes = SampleWebApplication. class ) public class SamplePropertyLoadingTest { @Autowired private SampleProperty sampleProperty; @Value ( "${prefix.stringProp1}" ) private String stringProp1; @Test public void testLoadingOfProperties() { System.out.println( "stringProp1 = " + stringProp1); assertThat(sampleProperty.getStringProp1(), equalTo( "propValue1" )); assertThat(sampleProperty.getStringProp2(), equalTo( "propValue2" )); assertThat(sampleProperty.getIntProp1(), equalTo( 10 )); assertThat(sampleProperty.getListProp(), hasItems( "listValue1" , "listValue2" )); assertThat(sampleProperty.getMapProp(), allOf(hasEntry( "key1" , "mapValue1" ), hasEntry( "key2" , "mapValue2" ))); } } |
If you are interested in exploring this sample further, I have a github repo with the code checked in here.