Start Learning
Javaneer
Back to stage
Module 0·Spring Foundations

Properties, Profiles & the Environment

Externalize configuration with properties, bind it to typed objects, and switch behavior per environment with profiles.

16 min readIntermediate
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: 5

Typed 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.

Configuration is the dial panel, not the wiring

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=prod

Profiles 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.

One jar, three environments

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.
Was this lesson helpful?
Edit this page on GitHub