@ConfigurationProperties

Binds every property under a prefix to a typed object, usually a record, with relaxed names, conversion and optional validation.

Signature
@ConfigurationProperties("app.payments")  |  @ConfigurationProperties(prefix = "app.payments", ignoreUnknownFields = false)
  • Added in Spring Boot Spring Boot 1.0.0

Parameters

ParameterTypeDescription
$value / prefixoptionalString= ""The property prefix to bind, in canonical kebab-case form such as app.payments.
$ignoreInvalidFieldsoptionalboolean= falseWhether values that cannot be converted are skipped instead of failing startup.
$ignoreUnknownFieldsoptionalboolean= trueWhether properties under the prefix with no matching field are ignored.

Return value

n/aBinds external configuration under the prefix to the annotated type (or to the object returned by an annotated @Bean method).

Errors

Binding errors (unconvertible values, failed validation) stop startup with a report naming the property and its source.

Changelog

  • PHP Spring Boot 3.0.0A type with a single parameterised constructor, such as a record, binds through the constructor without @ConstructorBinding; @ConstructorBinding moved to org.springframework.boot.context.properties.bind.

@ConfigurationProperties maps a whole block of settings onto one Java type. Give it a prefix, and every property under that prefix binds to a matching component or field. Records are the natural fit: immutable, concise, and bound through their constructor.

@ConfigurationProperties("app.payments")
record PaymentProperties(URI baseUrl, Duration timeout, int maxRetries) { }

Registering the type

The annotation alone does not create a bean. Register the type in one of three ways: list it in @EnableConfigurationProperties(PaymentProperties.class) on a configuration class, add @ConfigurationPropertiesScan to the main class so every annotated type under it is found, or put the annotation on a @Bean method to bind onto an object you create yourself. Forgetting this step is the usual reason for "no qualifying bean of type PaymentProperties".

Relaxed binding and conversion

baseUrl binds from app.payments.base-url in a properties file and from APP_PAYMENTS_BASEURL as an environment variable. Write the prefix in kebab-case; camelCase prefixes are rejected. Values are converted to the component types: Duration accepts 10s or PT10S, DataSize accepts 10MB, and URI, enums, lists and maps all work. Use @DefaultValue on a record component for a default.

Validate at startup

Add @Validated and Jakarta constraints such as @NotNull or @Min (with the validation starter on the classpath). A bad or missing setting then fails startup with a clear report instead of a NullPointerException during the first payment.

Mistakes to avoid

  • A misspelt key is ignored by default, because ignoreUnknownFields is true. Set it to false for a prefix you fully own when typos would be costly, and add spring-boot-configuration-processor so the IDE completes and checks your keys.
  • Using a prefix under spring. or server. for your own settings. Keep yours under a namespace such as app..
  • Injecting the properties object into dozens of classes. Pass the specific values into the beans that need them, so each class states what it depends on.

Example

Example · java
import java.net.URI;
import java.time.Duration;

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

@ConfigurationProperties("app.payments")
@Validated
record PaymentProperties(
        @NotNull URI baseUrl,
        @DefaultValue("10s") Duration timeout,
        @DefaultValue("3") @Min(0) @Max(10) int maxRetries) {
}

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PaymentProperties.class)
class PaymentConfig {
}

@Service
public class PaymentService {

    private final PaymentProperties props;

    public PaymentService(PaymentProperties props) {
        this.props = props;
    }

    public String describe() {
        return "Calling " + props.baseUrl() + " with timeout " + props.timeout()
                + " and up to " + props.maxRetries() + " retries";
    }
}

When to use it

  • Holding the base URL, timeout and retry count for a payment provider client
  • Validating required settings at startup so a bad deployment fails before taking traffic
  • Binding settings onto a library object that only offers setters

Frequently asked questions

Why is my @ConfigurationProperties record not a bean?

The annotation does not register anything on its own. Add @EnableConfigurationProperties(YourType.class) to a configuration class or @ConfigurationPropertiesScan to the main class.

Do records need @ConstructorBinding?

No. Since Spring Boot 3.0 a type with a single constructor binds through it automatically. @ConstructorBinding is only needed to pick one of several constructors.

How do I set a default value on a record component?

Annotate the component with @DefaultValue("...") from org.springframework.boot.context.properties.bind. The string is converted like any other property value.

More examples

The YAML that binds to it

Kebab-case keys bind to camelCase components. In production, APP_PAYMENTS_BASEURL or APP_PAYMENTS_MAXRETRIES set as environment variables override these values.

Example · yaml
app:
  payments:
    base-url: https://payments.internal.example/v2
    timeout: 4s
    max-retries: 5

Binding onto a third-party object from a @Bean method

Spring creates the object, then binds app.sftp.host, app.sftp.port and app.sftp.user onto it through its setters. This works for any class with JavaBean setters, including ones you cannot annotate.

Example · java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// A class from a library, configured through setters.
class SftpSettings {
    private String host;
    private int port = 22;
    private String user;

    public String getHost() { return host; }
    public void setHost(String host) { this.host = host; }
    public int getPort() { return port; }
    public void setPort(int port) { this.port = port; }
    public String getUser() { return user; }
    public void setUser(String user) { this.user = user; }
}

@Configuration(proxyBeanMethods = false)
public class SftpConfig {

    @Bean
    @ConfigurationProperties("app.sftp")
    SftpSettings sftpSettings() {
        return new SftpSettings();
    }
}

Scanning for every properties record

@ConfigurationPropertiesScan registers every @ConfigurationProperties type under the main package. ignoreUnknownFields = false on the CORS record turns a typo such as app.cors.allowed-origin into a startup failure instead of an empty list.

Example · java
import java.util.List;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
@ConfigurationPropertiesScan
public class StoreApplication {

    public static void main(String[] args) {
        SpringApplication.run(StoreApplication.class, args);
    }
}

@ConfigurationProperties("app.catalogue")
record CatalogueProperties(int pageSize, List<String> featuredCategories) {
}

@ConfigurationProperties(prefix = "app.cors", ignoreUnknownFields = false)
record CorsProperties(List<String> allowedOrigins) {
}

Discussion

  • Be the first to comment on this lesson.