Spring Boot
ConfigurationProperties annotation
Default Values
Java
Configuration Binding

ConfigurationProperties default values in a bound class

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When using @ConfigurationProperties in Spring Boot, default values are set by assigning initial values to the fields of your configuration class. If a property is not defined in application.properties or application.yml, the field retains its Java-assigned default. This is simpler and more reliable than using @Value with default syntax (${prop:default}), because the defaults live directly in the configuration class alongside their field types and validation annotations.

Basic Default Values

java
1import org.springframework.boot.context.properties.ConfigurationProperties;
2
3@ConfigurationProperties(prefix = "app")
4public class AppProperties {
5
6    private String name = "MyApp";           // Default: "MyApp"
7    private int maxRetries = 3;              // Default: 3
8    private long timeoutMs = 5000;           // Default: 5000
9    private boolean debugMode = false;       // Default: false
10    private String apiUrl = "https://api.example.com";
11
12    // Getters and setters required for binding
13    public String getName() { return name; }
14    public void setName(String name) { this.name = name; }
15
16    public int getMaxRetries() { return maxRetries; }
17    public void setMaxRetries(int maxRetries) { this.maxRetries = maxRetries; }
18
19    public long getTimeoutMs() { return timeoutMs; }
20    public void setTimeoutMs(long timeoutMs) { this.timeoutMs = timeoutMs; }
21
22    public boolean isDebugMode() { return debugMode; }
23    public void setDebugMode(boolean debugMode) { this.debugMode = debugMode; }
24
25    public String getApiUrl() { return apiUrl; }
26    public void setApiUrl(String apiUrl) { this.apiUrl = apiUrl; }
27}
yaml
1# application.yml — only override what you need
2app:
3  name: ProductionApp
4  max-retries: 5
5  # timeout-ms not specified — uses default 5000
6  # debug-mode not specified — uses default false
7  # api-url not specified — uses default https://api.example.com

Enabling the Configuration Class

java
1import org.springframework.boot.context.properties.EnableConfigurationProperties;
2import org.springframework.context.annotation.Configuration;
3
4@Configuration
5@EnableConfigurationProperties(AppProperties.class)
6public class AppConfig {
7}
8
9// Or use @ConfigurationPropertiesScan on the main class (Spring Boot 2.2+)
10@SpringBootApplication
11@ConfigurationPropertiesScan
12public class Application {
13    public static void main(String[] args) {
14        SpringApplication.run(Application.class, args);
15    }
16}

Collection Defaults

java
1@ConfigurationProperties(prefix = "app")
2public class AppProperties {
3
4    private List<String> allowedOrigins = List.of("http://localhost:3000");
5    private Map<String, String> headers = Map.of(
6        "X-App-Name", "MyApp",
7        "X-App-Version", "1.0"
8    );
9    private Set<String> enabledFeatures = new HashSet<>(Set.of("auth", "logging"));
10
11    // Getters and setters
12    public List<String> getAllowedOrigins() { return allowedOrigins; }
13    public void setAllowedOrigins(List<String> allowedOrigins) {
14        this.allowedOrigins = allowedOrigins;
15    }
16
17    public Map<String, String> getHeaders() { return headers; }
18    public void setHeaders(Map<String, String> headers) {
19        this.headers = headers;
20    }
21
22    public Set<String> getEnabledFeatures() { return enabledFeatures; }
23    public void setEnabledFeatures(Set<String> enabledFeatures) {
24        this.enabledFeatures = enabledFeatures;
25    }
26}
yaml
1# Overriding replaces the entire collection, not merges
2app:
3  allowed-origins:
4    - https://example.com
5    - https://app.example.com
6  # headers not specified — uses default map

Nested Object Defaults

java
1@ConfigurationProperties(prefix = "app")
2public class AppProperties {
3
4    private final Database database = new Database();
5    private final Cache cache = new Cache();
6
7    public Database getDatabase() { return database; }
8    public Cache getCache() { return cache; }
9
10    public static class Database {
11        private String url = "jdbc:h2:mem:testdb";
12        private int poolSize = 10;
13        private long connectionTimeoutMs = 30000;
14
15        // Getters and setters
16        public String getUrl() { return url; }
17        public void setUrl(String url) { this.url = url; }
18        public int getPoolSize() { return poolSize; }
19        public void setPoolSize(int poolSize) { this.poolSize = poolSize; }
20        public long getConnectionTimeoutMs() { return connectionTimeoutMs; }
21        public void setConnectionTimeoutMs(long ms) { this.connectionTimeoutMs = ms; }
22    }
23
24    public static class Cache {
25        private boolean enabled = true;
26        private int ttlSeconds = 300;
27
28        public boolean isEnabled() { return enabled; }
29        public void setEnabled(boolean enabled) { this.enabled = enabled; }
30        public int getTtlSeconds() { return ttlSeconds; }
31        public void setTtlSeconds(int ttlSeconds) { this.ttlSeconds = ttlSeconds; }
32    }
33}
yaml
1# Override only specific nested properties
2app:
3  database:
4    url: jdbc:postgresql://localhost:5432/mydb
5    pool-size: 20
6    # connection-timeout-ms uses default 30000
7  # cache section not specified — all defaults apply

Constructor Binding (Spring Boot 2.2+)

With @ConstructorBinding, defaults are specified as constructor parameter defaults (Kotlin) or via @DefaultValue:

java
1import org.springframework.boot.context.properties.ConfigurationProperties;
2import org.springframework.boot.context.properties.bind.DefaultValue;
3
4@ConfigurationProperties(prefix = "app")
5public record AppProperties(
6    @DefaultValue("MyApp") String name,
7    @DefaultValue("3") int maxRetries,
8    @DefaultValue("5000") long timeoutMs,
9    @DefaultValue("false") boolean debugMode
10) {}
kotlin
1// Kotlin — natural default parameter syntax
2@ConfigurationProperties(prefix = "app")
3data class AppProperties(
4    val name: String = "MyApp",
5    val maxRetries: Int = 3,
6    val timeoutMs: Long = 5000,
7    val debugMode: Boolean = false
8)

Validation with Defaults

java
1import jakarta.validation.constraints.Min;
2import jakarta.validation.constraints.NotBlank;
3import org.springframework.validation.annotation.Validated;
4
5@Validated
6@ConfigurationProperties(prefix = "app")
7public class AppProperties {
8
9    @NotBlank
10    private String name = "MyApp";
11
12    @Min(1)
13    private int maxRetries = 3;
14
15    @Min(100)
16    private long timeoutMs = 5000;
17
18    // If someone sets app.max-retries=0 in properties,
19    // validation fails with ConstraintViolationException
20}

Add spring-boot-starter-validation dependency for JSR-303 validation:

groovy
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-validation'
}

Common Pitfalls

  • Missing setter methods: @ConfigurationProperties uses setter-based binding by default. Without setters, Spring cannot override the defaults from properties files. The field keeps its Java default, and no error is thrown — the property is silently ignored.
  • Collection defaults replaced, not merged: When you specify app.allowed-origins in YAML, the entire list replaces the default. Spring does not merge the new values with the defaults. If you need additive behavior, handle it in application code.
  • @DefaultValue only works with constructor binding: The @DefaultValue annotation is for record-style or constructor-based binding. Using it on a field of a mutable class has no effect. For mutable classes, assign the default directly to the field.
  • Null defaults for wrapper types: private Integer count; defaults to null, not 0. If code accesses count without null checking, it throws NullPointerException. Use primitive types for required numeric properties or initialize wrapper types explicitly.
  • Property name binding rules: Spring Boot uses relaxed binding: app.maxRetries, app.max-retries, app.max_retries, and APP_MAXRETRIES all bind to maxRetries. However, in application.properties/application.yml, kebab-case (max-retries) is the recommended convention.

Summary

  • Set defaults by assigning initial values to fields in the @ConfigurationProperties class
  • Only properties explicitly defined in application.properties/application.yml override the defaults
  • Use @DefaultValue for constructor-binding or record classes
  • Nested objects get defaults through field initialization of inner static classes
  • Collections are fully replaced when overridden, not merged with defaults
  • Add @Validated with JSR-303 annotations to enforce constraints on both defaults and overrides

Course illustration
Course illustration

All Rights Reserved.