Spring Boot / Basic: composition and HTTP contracts / Lesson 3
External configuration and profiles
Bind a small typed configuration contract, explain source precedence and profile activation, and keep secrets and deployment policy outside accidental defaults.
On this page
Lesson goals
- Use validated ConfigurationProperties for related settings.
- Explain effective values from layered sources.
- Test profile and secret boundaries without printing credentials.
Treat settings as an application input API
A setting has a type, unit, requiredness, accepted range, and owner. A timeout of2 without a unit is not a complete contract. Group related application settings through @ConfigurationProperties instead of scattering unrelated @Value strings through services. Constructor-bound records make a compact immutable settings model, while validation can reject a configuration that would otherwise fail only under traffic.
Configuration is another trust boundary: a value can be syntactically bindable yet operationally unreasonable. A page-size limit of a million might be an int but violate memory and latency budgets. Validate cross-field rules explicitly where annotations do not express them. Avoid using a convenient default to hide the absence of a required deployment decision.
Register and validate one properties model
The Java fragment declares a page bound and a typed Duration, then registers the model explicitly. It requires the Boot3.5 configuration and validation dependencies, so it remains a framework illustration. Registration matters: an annotated type that is never scanned or enabled is not automatically a usable injected bean. Record constructor binding in this baseline does not require copying older setter-style patterns.
The minimum and maximum check the page limit, while @NotNull only checks timeout presence. A positive-duration rule still needs an appropriate constraint or explicit check. Validate the actual contract instead of assuming every field annotation covers every domain rule. An exception should identify the setting safely without printing secret values from the entire environment.
package training.catalog;
import java.time.Duration;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties("catalog")
@Validated
record CatalogProperties(@Min(1) @Max(200) int maxPageSize,
@NotNull Duration timeout) {}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(CatalogProperties.class)
class CatalogConfiguration {}
The page range and timeout presence are declared; positive Duration is a separate rule. Container binding/validation was not executed.
Explain which profile document changes which value
The YAML has a base document and a training-activated overlay. When that profile is active in a correctly integrated application, the intended max-page-size is20 while timeout stays2s. This is a conditional configuration explanation, not a reported startup observation. A YAML parser can verify structure but cannot prove Boot's complete binding, source order, or bean validation.
Profiles select configuration, not authorization. A profile called production does not secure an endpoint, and an active profile can also create or remove beans. Keep activation decisions visible. Profile-specific documents have restrictions on where activation properties can be defined; do not place spring.profiles.active anywhere that happens to look convenient and assume it is always valid.
catalog:
max-page-size: 50
timeout: 2s
---
spring:
config:
activate:
on-profile: training
catalog:
max-page-size: 20
The intended overlay changes only the page limit. Syntax parsing does not execute profile activation or verify the full Spring Environment.
Inspect effective sources without exposing secrets
Boot defines a documented property-source order. Packaged configuration, external files, environment, system properties, command-line arguments, and test overrides can supply different values; later sources in that order can override earlier ones. A checked-in file is therefore not necessarily the effective value. Tests and dynamic property sources add their own layers, so avoid an oversimplified claim that one source always wins.
Environment-variable relaxed-binding rules are specific: dots become underscores, dashes are removed, and names are uppercased. For catalog.max-page-size, the documented form is CATALOG_MAXPAGESIZE. Secrets should come through an approved distribution process and remain absent from logs and source artifacts. Do not print a complete connection string or token merely to diagnose precedence.
Test effective policy instead of only file syntax
Test defaults, overrides, absent required settings, invalid ranges, profile combinations, and secret-safe failure responses. A restricted ApplicationContextRunner or another scoped framework fixture can exercise binding without a network listener when suitable dependencies and configuration are available. Such fixtures here remain illustrative unless explicitly run; no actual service environment is loaded.
Record the resolved source origin and a redacted value category rather than sensitive content. Keep configuration versioned with the application's compatibility contract, and review changes during rollout. A well-designed settings boundary makes an invalid deployment fail early and understandably while allowing the same application artifact to run under deliberately different approved policies.
Common errors and fixes
ConfigurationPropertiesBindException
- Why it happens
- A required or convertible setting cannot satisfy the binding/validation contract.
- How to fix it
- Inspect the precise property and active source without printing all values.
- Prevent it next time
- Test absent, malformed, and boundary settings.
An environment override appears ignored
- Why it happens
- The name or effective source order differs from the assumption.
- How to fix it
- Use documented relaxed names and inspect source origin safely.
- Prevent it next time
- Test configuration through the real binding boundary.
A profile unexpectedly removed a service bean
- Why it happens
- Profile-based composition changed, not only a scalar setting.
- How to fix it
- Review active profiles and conditional registrations together.
- Prevent it next time
- Include profile combinations in context tests.
Check your understanding
1. Does @NotNull Duration guarantee a positive timeout?
Show worked solution
No. It checks presence. Zero or negative values need a separate supported constraint or explicit validation. Define the unit and accepted bound, and test those cases before using the value to configure an outbound client or executor.
2. A parser accepts both YAML documents. What remains unverified?
Show worked solution
Actual Spring source precedence, profile activation, properties registration, conversion, validation, and the components consuming the values. Syntax success is useful but narrower than an application-context test; do not describe it as evidence of a successful deployment.
Key takeaways
- Configuration is typed input with units and ownership.
- Effective values depend on documented source layering.
- Profiles, secrets, syntax, and binding need distinct checks.