Properties, Profiles & the Environment
Externalize configuration with properties, bind it to typed objects, and switch behavior per environment with profiles.
On this page
Hard-coding a database URL or an API key into your source is a cardinal sin: the same build has to run on your laptop, a test server, and production, each with different settings. Spring's answer is externalized configuration - values live outside the code, and beans read them in.
Reading a single value with @Value
The simplest tool injects one property into a field or parameter:
@Service
class CatalogService {
CatalogService(@Value("${bookvault.page-size:20}") int pageSize) {
// "${...}" reads a property; ":20" is the default if it's absent
}
}The property itself lives in application.properties or application.yml:
bookvault:
page-size: 50
featured-limit: 5Typed configuration with @ConfigurationProperties
Sprinkling @Value everywhere gets messy. The professional approach binds a whole
group of related properties to a typed object:
@ConfigurationProperties(prefix = "bookvault")
record BookVaultProperties(int pageSize, int featuredLimit) { }Now inject BookVaultProperties like any bean and read properties.pageSize() -
type-safe, autocompleted, validated, and documented in one place.
Enable it once
Add @ConfigurationPropertiesScan to your main application class (or
@EnableConfigurationProperties) and Spring binds and registers these records
automatically. Invalid or missing required values fail at startup, not in production.
A washing machine's behavior is fixed in its wiring, but a dial panel lets you choose temperature and spin speed without rewiring anything. Externalized configuration is that dial panel: the same compiled application behaves differently per environment, just by turning the dials - no recompile.
Profiles: whole configurations per environment
Sometimes you need more than different values - you need different beans. An in-memory database for local development, a real one in production; a fake email sender in tests, a real SMTP client live. Profiles switch entire sets of beans on and off.
@Component
@Profile("dev")
class InMemoryBookRepository implements BookRepository { } // only in 'dev'
@Component
@Profile("prod")
class PostgresBookRepository implements BookRepository { } // only in 'prod'You choose the active profile outside the code - so the same jar runs anywhere:
# application.properties, or an env var, or a command-line flag
spring.profiles.active=prodProfiles also select property files: application-dev.yml and
application-prod.yml layer on top of the base application.yml for the active
profile.
Don't put secrets in application.yml
Property files are for configuration, not secrets. Passwords, API keys, and
tokens belong in environment variables or a secrets manager, referenced as
${DB_PASSWORD}. We'll return to this in the Security and Production modules.
BookVault must run locally with an in-memory store, in CI with a throwaway test database, and in production against PostgreSQL - all from the same build artifact. Sketch how you'd use profiles and property files to achieve this without changing or recompiling the code between environments.
What do Spring profiles let you switch between per environment?
Key takeaways
- Externalize configuration so one build runs in every environment; never hard-code environment-specific values.
- @Value injects a single property (with an optional default); @ConfigurationProperties binds a group of properties to a typed, validated object - the preferred approach.
- Profiles switch entire sets of beans and property files (application-<profile>.yml) on or off per environment.
- Choose the active profile from outside the code via spring.profiles.active (env var or launch flag).
- Keep secrets out of property files - use environment variables or a secrets manager.