What is @ConfigurationProperties, and how does it differ from @Value?

8 minintermediateconfigurationpropertiesvalueconfiguration

Quick Answer

@Value injects a single configuration property into a single field, using a SpEL/placeholder expression, with no built-in validation or grouping. @ConfigurationProperties binds an entire group of related, hierarchical properties (a prefix like app.mail.*) onto a strongly-typed POJO or record in one step, supporting nested objects, lists, relaxed binding (kebab-case/camelCase/etc. interchangeably), and integration with Bean Validation — making it the better choice for anything beyond a single, standalone value.

Detailed Answer

Both read values from application.properties/application.yml, but at very different granularity:

@Value injects a single property into a single field, via a placeholder expression (which also supports full SpEL):

@Component
class MailService {
    @Value("${app.mail.host}")
    private String host;
    @Value("${app.mail.port:25}") // default value if not set
    private int port;
}

This gets unwieldy fast for anything with more than a couple of related values — no grouping, no nested structure, no built-in validation.

@ConfigurationProperties binds an entire group of hierarchical properties onto a dedicated, strongly-typed class in one step:

app:
  mail:
    host: smtp.example.com
    port: 587
    recipients:
      - alice@example.com
      - bob@example.com
@ConfigurationProperties(prefix = "app.mail")
public record MailProperties(String host, int port, List<String> recipients) { }
@Configuration
@EnableConfigurationProperties(MailProperties.class) // or annotate MailProperties with @Component directly
class MailConfig { }

Advantages of @ConfigurationProperties over scattered @Value fields:

  • Grouping and structure — nested objects, lists, and maps bind naturally, instead of one @Value per leaf property.
  • Relaxed bindingapp.mail.host, app.mail-host, and APP_MAIL_HOST (environment variable form) all bind to the same property, letting the same configuration class work across .yml, .properties, and environment-variable-based configuration (common in containerized deployments) without code changes.
  • Validation support — combining it with @Validated and Bean Validation annotations (@NotBlank, @Min) lets Spring Boot fail fast at startup if required configuration is missing or malformed, rather than surfacing a NullPointerException deep in application logic later.
  • IDE tooling — Spring Boot can generate metadata (spring-configuration-metadata.json) giving autocomplete and documentation for these properties in IDEs.

Rule of thumb: use @Value for a genuinely standalone, one-off property; use @ConfigurationProperties for anything with more than one or two related values, or anything you'd want validated at startup.