skip to content

When should you choose @Value over @ConfigurationProperties for binding configuration, and what are the tradeoffs?

level: middleimportance: should knowfreq 60%

answer

  1. @Value = one value or SpEL; @ConfigurationProperties = typed group
  2. Relaxed binding + validation + metadata = @ConfigurationProperties
  3. @Value = exact key only, no relaxed binding, no SpEL in @ConfigurationProperties
  4. Many @Values in one class = smell → use a properties record
  5. Both read Environment + ConversionService

basics

~20 s

Use @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
java
// @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

for a junior

Know @Value is for single values; @ConfigurationProperties groups many.

for a middle

List the concrete advantages: relaxed binding, validation, nested types, testability, and note @Value's SpEL edge.

for a senior

Give a clear decision rule and mention enabling mechanisms (@EnableConfigurationProperties/@ConfigurationPropertiesScan) and immutable record binding.

for a principal

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)

context