Configuration & Profiles

Where values come from

Spring reads many sources and lets later ones win:

  1. command line — --server.port=9000
  2. environment variables — SERVER_PORT=9000
  3. application-{profile}.properties
  4. application.properties
  5. defaults in code
java -jar app.jar --server.port=9000
SERVER_PORT=9000 java -jar app.jar

Relaxed binding maps server.port, SERVER_PORT and server-port to the same property.

Reading single values

@Component
class Mailer {
    private final String from;

    Mailer(@Value("${app.mail.from:noreply@example.com}") String from) {
        this.from = from;
    }
}

The part after : is the default.

Typed configuration

Better for anything with more than one key:

@ConfigurationProperties("app.mail")
public record MailProperties(String from, String host, int port, boolean tls) {}
@SpringBootApplication
@EnableConfigurationProperties(MailProperties.class)
public class DemoApplication {  }
app.mail.from=noreply@example.com
app.mail.host=smtp.example.com
app.mail.port=587
app.mail.tls=true

Inject MailProperties like any other bean. Records give you immutability and constructor binding for free.

Tip

Add spring-boot-configuration-processor as an optional dependency and your IDE autocompletes your own properties.

Validating configuration

@Validated
@ConfigurationProperties("app.mail")
public record MailProperties(@Email String from, @NotBlank String host,
                             @Min(1) @Max(65535) int port) {}

A bad value now fails at startup, not at the first send.

Profiles

A profile is a named set of configuration.

application.properties            always applied
application-dev.properties        only with the dev profile
application-prod.properties       only with the prod profile
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev
java -jar app.jar --spring.profiles.active=prod
SPRING_PROFILES_ACTIVE=prod java -jar app.jar

Beans can be profile-specific:

@Bean
@Profile("!prod")
Notifier consoleNotifier() { return msg -> System.out.println(msg); }

Secrets

Never commit passwords. Pass them as environment variables and reference them:

spring.datasource.password=${DB_PASSWORD}
Warning

application.properties ends up inside the jar. Anything environment-specific or secret belongs outside it.

Useful built-in properties

PropertyEffect
server.portHTTP port, 0 = random
spring.application.namename in logs, metrics, traces
logging.level.<package>log level per package
logging.console.enablednew in Boot 4: turn console logging off
spring.threads.virtual.enabledvirtual threads for request handling
spring.jpa.hibernate.ddl-autoschema handling, none in production

★ Exercises

  1. Move a hard-coded string into application.properties and inject it with @Value.
  2. Convert it to a @ConfigurationProperties record with three keys.
  3. Add @Validated and make startup fail on an empty value.
  4. Create application-dev.properties with logging.level.com.example=DEBUG and run with that profile.
  5. Override the port three ways: file, environment variable, command line. Which wins?