Configuration Management with Environment Variables
In Spring Boot, properties can be managed using various sources such as properties files, YAML files, and environment variables. This guide explains how to use application.properties
and application.yml
files in conjunction with environment variables to manage configuration properties in Java code using the @Value
annotation. Additionally, it covers how properties can be overridden via HTTP headers or query parameters in REST Controllers, the importance of documenting property metadata, and best practices for using @Value
annotations.
Properties Files
application.properties
Spring Boot allows you to define properties in the application.properties
file located in the src/main/resources
directory.
Example:
my.property=valueFromPropertiesFile
application.yml
Alternatively, you can use the application.yml
file for defining properties.
Example:
my: property: valueFromYamlFile
Using @Value Annotation
You can inject these properties into your Spring components using the @Value
annotation.
Example:
import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;
@Componentpublic class MyComponent {
@Value("${my.property}") private String myProperty;
public String getMyProperty() { return myProperty; }}
Best Practice: Using @Value in @Configuration Classes
While @Value
annotations can be used without @Configuration
annotations, if the same property is used in multiple places in the code, it is better and more type-safe to define the @Value
once in a @Configuration
class and then use that injected class in different places in custom code.
Example:
-
Define properties in a configuration class:
import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Configuration;@Configurationpublic class MyConfig {@Value("${my.property}")private String myProperty;public String getMyProperty() {return myProperty;}} -
Use the configuration class in other components:
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;@Componentpublic class MyComponent {private final MyConfig myConfig;@Autowiredpublic MyComponent(MyConfig myConfig) {this.myConfig = myConfig;}public String getProperty() {return myConfig.getMyProperty();}}
This approach enhances type safety and centralizes the configuration properties, making it easier to manage and update them.
Overriding Properties
Command Line Arguments
Properties defined in application.properties
or application.yml
can be overridden using command line arguments.
Example:
java -jar myapp.jar --my.property=overriddenValueFromCLI
Or using the -D
flag:
java -Dmy.property=overriddenValueFromCLI -jar myapp.jar
Environment Variables
The preferred method for overriding properties, especially for secrets and environment-specific configurations, is using environment variables.
Example:
export MY_PROPERTY=overriddenValueFromEnv
To use environment variables in your properties file, you can define them with a default value:
application.properties
my.property=${MY_PROPERTY:defaultValueFromPropertiesFile}
application.yml
my: property: ${MY_PROPERTY:defaultValueFromYamlFile}
In this setup, the environment variable MY_PROPERTY
will override the value defined in the properties or YAML file.
Automatic Property Resolution from Environment Variables
If a property is not defined in the application.properties
or application.yml
file, Spring Boot will automatically look for an environment variable following a specific naming convention to resolve the property.
Naming Convention
Spring Boot maps environment variables to properties by replacing dots (.
) in property names with underscores (_
) and converting the property name to uppercase.
For example, if you have a property my.property
, Spring Boot will look for an environment variable named MY_PROPERTY
.
Example
-
Define a property in code:
import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Componentpublic class MyComponent {@Value("${my.property}")private String myProperty;public String getMyProperty() {return myProperty;}} -
Environment variable (if property is missing from files):
Terminal window export MY_PROPERTY=valueFromEnvIfMissingInProperties
In this example, if my.property
is not defined in application.properties
or application.yml
, Spring Boot will automatically use the value from the environment variable MY_PROPERTY
.
Profiles
Spring Boot supports profiles to define different configurations for different environments. Profiles can be activated via environment variables or command line arguments.
Defining Profiles
application-dev.properties
my.property=valueFromDevPropertiesFile
application-prod.properties
my.property=valueFromProdPropertiesFile
Activating Profiles
Profiles can be activated by setting the spring.profiles.active
environment variable or using command line arguments.
Example:
export SPRING_PROFILES_ACTIVE=prod
Or
java -jar myapp.jar --spring.profiles.active=prod
Overriding Properties in REST Controllers
In addition to using properties files and environment variables, some properties can be overridden in REST Controllers via HTTP headers or query parameters. This can be useful for dynamic configuration and testing purposes.
Example
-
Controller Code:
import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestHeader;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class MyController {@GetMapping("/config")public String getConfig(@RequestHeader(value = "X-My-Property", required = false) String headerValue,@RequestParam(value = "myProperty", required = false) String queryValue) {String propertyValue = headerValue != null ? headerValue : (queryValue != null ? queryValue : "defaultValue");return "Property Value: " + propertyValue;}} -
OpenAPI Documentation:
The properties overridden via HTTP headers or query parameters should be documented in the OpenAPI (Swagger) documentation for better visibility and usage by API consumers.
paths:/config:get:summary: Retrieve configurationparameters:- name: X-My-Propertyin: headerrequired: falseschema:type: stringdescription: Overrides my.property value from HTTP header- name: myPropertyin: queryrequired: falseschema:type: stringdescription: Overrides my.property value from query parameterresponses:'200':description: successful operationcontent:application/json:schema:type: string
Documenting Property Metadata
When creating a property, it’s crucial to document its metadata and purpose for better maintainability and understanding. This documentation should include the property’s name, default value, description, and any constraints.
Example Metadata Documentation
application.properties
# Default Value: defaultValue# Description: This property controls the behavior of XYZ component.# Constraints: Must be a non-empty string.
my.property=defaultValue
application.yml
# Property: my.property# Default Value: defaultValue# Description: This property controls the behavior of XYZ component.# Constraints: Must be a non-empty string.
my: property: defaultValue
Using @ConfigurationProperties for Grouping Related Properties
For grouping related properties, you can use the @ConfigurationProperties
annotation along with a metadata file.
-
Define the properties class:
import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Configuration;@Configuration@ConfigurationProperties(prefix = "my")public class MyProperties {private String property;public String getProperty() {return property;}public void setProperty(String property) {this.property = property;}} -
Define the metadata file (
src/main/resources/META-INF/spring-configuration-metadata.json
):{"properties": [{"name": "my.property","type": "java.lang.String","description": "This property controls the behavior of XYZ component.","defaultValue": "defaultValue","sourceType": "com.example.MyProperties"}]}
By following these practices, you can ensure a flexible, secure, and well-documented configuration management strategy in your Spring Boot applications.