Configuration in Spring Boot
application.yml, config sources and precedence, relaxed binding, and typed @ConfigurationProperties in a Boot app.
On this page
The Foundations module introduced properties, @ConfigurationProperties, and
profiles at the framework level. Spring Boot builds a rich, layered configuration
system on top - so the same BookVault jar behaves correctly on your laptop, in CI,
and in production, without a rebuild.
application.yml
Boot automatically loads application.properties or application.yml from
src/main/resources. YAML is the common choice for its nesting:
server:
port: 8080
spring:
application:
name: bookvault
bookvault:
loan-days: 14
max-active-loans: 5Anything Boot or a starter understands (like server.port) is picked up
automatically; your own keys (bookvault.*) are yours to read.
Where configuration comes from - and who wins
The same property can be set in many places. Boot merges them in a strict order of precedence, so a higher-priority source overrides a lower one. From lowest to highest, the ones you'll use most:
- Defaults in your code (
@ConfigurationPropertiesfield defaults) application.ymlpackaged in the jar- Profile-specific files (
application-prod.yml) - OS environment variables
- Command-line arguments
# override the packaged port at launch — no rebuild
java -jar bookvault.jar --server.port=9000
SERVER_PORT=9000 java -jar bookvault.jar # env var, same effectPicture the defaults printed on a bottom sheet, then a semi-transparent sheet for
application.yml laid on top, then one for the active profile, then environment
variables, then command-line args on top. Looking straight down, you see the topmost
value for each setting. Boot stacks the sheets; the highest one that has a value wins.
Relaxed binding
Boot is forgiving about names. The property bookvault.max-active-loans can be
supplied as an environment variable BOOKVAULT_MAXACTIVELOANS or
BOOKVAULT_MAX_ACTIVE_LOANS, or as bookvault.maxActiveLoans - all bind to the same
setting. This is relaxed binding, and it's why kebab-case in YAML maps cleanly to
SCREAMING_SNAKE_CASE environment variables in production.
Typed configuration, the Boot way
Rather than scattering @Value("${...}") reads, bind a whole group to a typed record
and inject it:
@ConfigurationProperties(prefix = "bookvault")
record BookVaultProperties(int loanDays, int maxActiveLoans) {}@Service
class LoanPolicy {
private final BookVaultProperties props;
LoanPolicy(BookVaultProperties props) { this.props = props; }
boolean canBorrowMore(int active) {
return active < props.maxActiveLoans(); // type-safe, autocompleted
}
}Enable and validate
Add @ConfigurationPropertiesScan to your main class so Boot finds and binds these
records. Add Bean Validation annotations (@Min, @NotBlank) to the record
components and Boot validates them at startup - a bad value fails fast instead of
surfacing in production.
BookVault's packaged application.yml sets server.port: 8080. Operations needs to
run it on port 9000 in production without rebuilding the jar or editing files
inside it. Give two different ways to do that at launch, and explain which source
precedence makes each work.
If server.port is set in the packaged application.yml AND passed as a command-line argument, which value wins?
Key takeaways
- Boot auto-loads application.yml/properties and layers many configuration sources by a strict precedence order.
- From low to high: code defaults < packaged application.yml < profile files < environment variables < command-line arguments.
- The same jar can be reconfigured at launch (e.g. --server.port or SERVER_PORT) with no rebuild.
- Relaxed binding maps kebab-case properties to SCREAMING_SNAKE_CASE env vars and camelCase alike.
- Bind related settings to a typed @ConfigurationProperties record, enable it with @ConfigurationPropertiesScan, and validate it to fail fast on bad values.