Spring Boot — Adding configuration properties
Overview
Allowing external configuration is an essential aspect in the development of both libraries and applications. They allow users to customize aspects of the codes behavior without actual code changes.
Spring has always offered ways to access configuration in various ways. Spring-Boot introduced the central application configuration in a application.properties or application.yaml file. Alongside this default location for application configuration, Spring-Boot also introduced a well defined mechanism for typed configuration access.
Configuration properties are easy to use and offer a powerful tool to work with external configuration.
Defining Configuration Properties
Configuration Properties are regular PoJos that are annotated with @ConfigurationProperties. Depending on the necessary interactions, they can be classes or records. Spring will automatically bind external configuration to the exposed properties. It will use setters, or a constructor to set values. To use a constructor, either provide a single parameterized constructor, which will be autodetected by Spring, or annotate one of several parameterized constructors with @ConstructorBinding.
A simple example using a record to model a single String typed property:
@ConfigurationProperties("app.config")
record ConfigProperties(String name){}Property Names
The example above illustrated how the @ConfigurationProperties annotation defines the base name of the bound properties. In the example Spring will try to bind properties starting with app.config. to the ConfigProperties object. The name property would therfore be bound from a configuration named app.config.name.
Nested Properties
Sometimes it is helpful to also define nested objects to bind properties to. This could be Collections or Maps, both of which Spring recognizes automatically. If the nested properties should have more structure than a simple list, also arbitrary objects are supported. The property than needs to be annotated with @NestedConfigurationProperty unless the type is a nested class in the class where the property is declared (in this case Spring will automatically assume a nested properties object).
The following example illustrates several ways to declare nested properties (the example uses annotations from project lombok to reduce boilerplate):
record Credentials(String name, String password) {}
@ConfigurationProperties("app.config")
@RequiredArgsConstructor
@Getter
public class MyProperties {
private final List<String> labels;
private final Map<String, Integer> colors;
@NestedConfigurationProperties
private final Credentials credentials;
private final Host host;
public record Host(String hostname, String domain) {}
} A configuration could look like this:
app.config.labels=tip,top
app.config.colors.blue=1
app.config.colors.green=2
app.config.credentials.name=admin
app.config.credentials.password=admin123
app.config.host.hostname=localhost
app.config.host.domain=localDefault Values
In some cases it is useful to provide default values for properties, so that users do not need to specify all properties. If properties are mutable, this can be done by assigning a value to the property. If there is no configuration to bind to the property, it will not be changed. For immutable properties the @DefaultValue annotation can be used. This however is limited to constant expressions.
Example:
@ConfigurationProperties("app.config")
@RequiredArgsConstructor
@Getter
public class MyProperties {
@Setter
private UUID myUuid = UUID.randomUUID()
@DefaultValue("development")
private final String environment;
}Validation
If configuration needs to meet requirements (e.g. a String needs to actually represent an E-Mail address), it is good practice to check these requirements at startup. To ease validation Spring supports using standard jakarta validation annotations, if a validation provider is available. To use this support, annotate the @ConfigurationProperties class with @Validated and annotate properties with the necessary constraints.
Example:
@Validated
@ConfigurationProperties("app.config")
record MyProperties(
@Email
String email,
@Min(10)
Integer number
) {}Adding documentation
While some configuration might just be self explanatory, often some documentation is helpful. Often documentation is added to the properties file, next to the properties. However this has several drawbacks:
- For libraries, there is no configuration file, as this will only be added wherever the library is used.
- Spring allows to split configuration into several files, which adds additional maintenance overhead
- Separating implementation and documentation increases the risk of stale documentation
With Springs Configuration Properties we can add regular java documentation to the properties. By adding the spring-boot-configuration-processor as described in Generating Your Own Metadata by Using the Annotation Processor, spring will generate additional metadata that contains files that allow editors to provide inline support for editing configuration files.
Properties not defined as Configuration Properties
Unfortunately it is not possible to use Configuration Properties everywhere where external configuration is referenced. E.g. when using @ConditionalOnProperty properties need to be referenced literal, because binding of Configuration Properties has not yet happened.
Get Christian Frommeyer’s stories in your inbox
Join Medium for free to get updates from this writer.
It’s good practice to not spread these property names too much over the code as this will cause maintenance issues.
Adding documentation
The good news is that it is still possible to add documentation for those properties in the same way as outlined above. The simple solution is to still define the property in a Configuration Properties object. Even if the value will never be read from the object, all other features are still available.
If this feels wrong, or there is no suitable Configuration Properties object, the documentation can also be added manually. Just add a file named META-INF/additional-spring-configuration-metadata.json and add the necessary entries as explained in Providing Manual Hints.
Using Configuration Properties
In order to use configuration properties, it is necessary to activate them. Spring does not treat the @ConfigurationPropeties annotation as a @Component annotation, and therefore Configuration Properties are not picked up by regular component scanning.
Activating Configuration Properties
There are several ways how Configuration Properties can be activated, All of them are described in detail in Externalized Configuration. My preferred way to enable them is to use the @EnableConfigurationProperties annotation on a Java-Config class. This ties the configuration to a specific part of the Configuration and therefor provides some documentation of where it will be used.
Example:
@Configuration
@EnableConfigurationProperties(MyProperties.class)
public class MyConfig {
@Bean
public MyService myService(MyProperties props) {
return new MyService(props);
}
}Reading from Configuration Properties
After enabling the properties are available for auto-wiring also in other places. Values can then be read from the properties object directly, or via additional methods added to the properties class.
While it is possible, writing to the properties object should be avoided, as this might have unwanted effects to other parts of the application.
Spring also offers ways to reload configuration without restarting the application. If this happens, the properties object will be swapped under the hood with a new one containing the potentially changed configuration. In order to make use of this mechanism it helps to read properties always from the properties object instead of copying the values to other places, as in this case Spring will not be able to swap the values automatically.
Test involving external configuration
Using Configuration Properties is also nice for testing. In many cases it provides a simple interface for setting up a test scenario without having to deal with Spring or other environment infrastructure. Just passing in an instance of the properties class is enough.
If a lager chunk of the application needs to be tested (e.g. using a @SpringBootTest) the usual tools to modify the environment apply. Some documentation can be found in Context Configuration with Test Property Sources.
Read More
More from the author
The following articles will add more aspects of Auto-Configuration. Solving additional problems and introduce additional options to help library consumers.
More from other authors
One nice property of the Java/Spring-Boot eco system is that the community is large and full of great articles. Here are some that will also provide additional insights:






