Typed configuration and profiles

Spring Boot · Practical · 14 min

Typed, validated configuration catches mistakes at startup instead of in production traffic.

For a group of related settings, define a class or record annotated @ConfigurationProperties(prefix = "app.mail"). Boot binds app.mail.host, app.mail.port, app.mail.from and nested objects, lists, maps and Durations (timeout=5s) to it with type conversion, and you inject it like any bean. Register it with @ConfigurationPropertiesScan on the main class or @EnableConfigurationProperties(MailProperties.class).

Benefits over scattered @Values: one place for related settings, IDE auto-completion (with the spring-boot-configuration-processor dependency), and validation at startup with @Validated plus constraints: a missing host fails fast with a clear message.

Profiles select environment-specific configuration. application-dev.properties and application-prod.properties are loaded in addition to application.properties when the dev or prod profile is active, and override it. Activate with spring.profiles.active=prod (often via the environment variable SPRING_PROFILES_ACTIVE). A single YAML file can hold several profile sections separated by --- with spring.config.activate.on-profile.

Beans can be profile-specific with @Profile("dev"), for example a fake payment gateway in development. Keep profiles few and about environments; feature switches belong in normal properties.

Example

# application.properties
app.mail.host=smtp.example.com
app.mail.port=587
app.mail.timeout=5s
app.mail.recipients[0]=ops@example.com
# application-dev.properties (active with SPRING_PROFILES_ACTIVE=dev)
app.mail.host=localhost
// MailProperties.java
@Validated
@ConfigurationProperties(prefix = "app.mail")
record MailProperties(@NotBlank String host,
                      @Min(1) int port,
                      @DefaultValue("10s") Duration timeout,
                      List<String> recipients) {}
@SpringBootApplication
@ConfigurationPropertiesScan
class App { /* main */ }
@Service
class Mailer {
    Mailer(MailProperties props) {
        System.out.println(props.host() + ":" + props.port() + " " + props.timeout());
        // dev profile -> localhost:587 PT5S
    }
}

Remember

  • @ConfigurationProperties binds a prefix to a typed record or class
  • @Validated + constraints fail fast at startup
  • application-{profile}.properties overrides the defaults for that profile
  • Activate profiles with SPRING_PROFILES_ACTIVE

Watch out for

  • Creating a profile per feature flag, leading to combinatorial configurations; use plain properties for flags.

Check yourself

  1. The dev profile is active. application.properties sets app.mail.host=a and application-dev.properties sets it to b. What is bound?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.typesafe-configuration-properties