When should you choose @Value over @ConfigurationProperties for binding configuration, and what are the tradeoffs?
answer
- @Value = one value or SpEL; @ConfigurationProperties = typed group
- Relaxed binding + validation + metadata = @ConfigurationProperties
- @Value = exact key only, no relaxed binding, no SpEL in @ConfigurationProperties
- Many @Values in one class = smell → use a properties record
- Both read Environment + ConversionService
basics
~20 sUse @Value for a few individual, unrelated settings or when you need SpEL. Use @ConfigurationProperties to bind a whole group of related properties into a typed object with relaxed binding, validation, and easy testing. @Value doesn't support relaxed binding or bean validation.
solid answer
~40 s`@Value` injects one property (or SpEL expression) per annotation — good for a handful of standalone settings or when you need computed/SpEL values. `@ConfigurationProperties` binds an entire prefix of properties onto a typed POJO/record: it supports *relaxed binding* (`my.max-size`, `MY_MAX_SIZE`, `myMaxSize` all match), nested objects, `List`/`Map` binding, JSR-303 validation via `@Validated`, and metadata/IDE hints. It is far easier to unit-test (construct the object directly). `@Value` has no relaxed binding, no built-in validation, and scatters configuration across the codebase. The rule of thumb: reach for `@ConfigurationProperties` for any cohesive group of settings; use `@Value` only for one-off values or SpEL. Both ultimately read from the `Environment` and use the `ConversionService` for type conversion.
code
java · 14 lines// @Value: fine for one-off
@Value("${app.name}") String appName;
// @ConfigurationProperties: cohesive, validated, immutable group
@ConfigurationProperties(prefix = "mail")
@Validated
public record MailProps(
@NotBlank String host,
@Min(1) int port,
List<String> recipients) {} // relaxed binding: mail.host, MAIL_PORT, mail.recipients[0]...
@Configuration
@EnableConfigurationProperties(MailProps.class)
class MailConfig {}go deeper
Know @Value is for single values; @ConfigurationProperties groups many.
List the concrete advantages: relaxed binding, validation, nested types, testability, and note @Value's SpEL edge.
Give a clear decision rule and mention enabling mechanisms (@EnableConfigurationProperties/@ConfigurationPropertiesScan) and immutable record binding.
Frame it as configuration governance: typed, validated, discoverable config surfaces vs scattered @Value; migration strategy and metadata tooling.
**Two binding styles.** - `@Value("${...}")` / `@Value("#{...}")` — point injection: each annotation pulls exactly one value (or evaluates one SpEL expression). No grouping, no schema. - `@ConfigurationProperties(prefix = "mail")` — binds every property under `mail.*` onto a typed object's fields/record components. **What @ConfigurationProperties adds.** - *Relaxed binding*: `mail.from-address`, `mail.fromAddress`, `MAIL_FROMADDRESS`, `mail.from_address` all bind to `fromAddress`. `@Value` matches only the exact key. - *Nested/complex types*: nested classes, `List`, `Set`, `Map`, arrays bind naturally from indexed/nested keys (`mail.recipients[0]`, `mail.headers.x`). - *Validation*: annotate the class `@Validated` and use JSR-303 (`@NotNull`, `@Min`, `@Email`); binding fails fast with a clear `BindValidationException`. - *Metadata*: `spring-boot-configuration-processor` generates `META-INF/spring-configuration-metadata.json` for IDE autocompletion. - *Immutability*: constructor/`record` binding with `@ConfigurationProperties` yields immutable config objects. - *Testability*: instantiate the POJO with plain values in tests — no Spring context needed. **What @Value has that @ConfigurationProperties lacks.** - SpEL (`#{...}`) support — `@ConfigurationProperties` is placeholder-only, not SpEL. - Simplicity for a single value: no extra class needed. **Shared mechanics.** Both draw values from the `Environment`/`PropertySource`s and convert via the `ConversionService` (`ApplicationConversionService`). Both honor `${...}` placeholders. The difference is *structure and features*, not the underlying property source. **Decision guide.** - One or two unrelated flags, or need SpEL/computation → `@Value`. - A cohesive set of settings for a component/feature → `@ConfigurationProperties` record with `@Validated`. - Avoid dozens of `@Value`s scattered across a class — that is the classic sign you should have a `@ConfigurationProperties` type. **Gotcha.** Mixing both for the same settings duplicates truth. Also `@ConfigurationProperties` needs enabling (`@EnableConfigurationProperties` or `@ConfigurationPropertiesScan`, or `@Component`), whereas `@Value` works anywhere injection happens.
- Does @ConfigurationProperties support SpEL like @Value does?No. @ConfigurationProperties supports ${...} placeholders and relaxed binding but not #{...} SpEL. If you need computation, use @Value or compute in code.
- What is relaxed binding and why does it matter?@ConfigurationProperties matches a property to a field across naming variants (kebab-case, camelCase, UPPER_SNAKE for env vars). It lets the same setting be expressed in application.yml and as an environment variable without exact key matching, which @Value cannot do.
saying these in an interview costs you the question
- Saying @ConfigurationProperties supports SpEL
- Claiming @Value supports relaxed binding or JSR-303 validation
- Using twenty @Value fields instead of a properties class
- Thinking they read from different property sources (they share the Environment)