Injecting Values, Collections & Properties
Injecting Values, Collections & Properties
Constructor and setter injection let you wire beans together. But real applications also need scalar configuration — timeouts, URLs, feature flags, pool sizes — and structured data like lists of allowed origins or maps of locale-to-currency codes. Spring provides two complementary mechanisms for this: the @Value annotation and the type-safe @ConfigurationProperties binding. Knowing when and how to use each one separates clean, maintainable configuration from a tangle of magic strings.
The @Value Annotation
@Value resolves a Spring Expression Language (SpEL) or property-placeholder expression and injects the result into a field, constructor parameter, or setter argument. The most common form uses the ${...} syntax to look up a key in the Environment, which aggregates all property sources (system properties, OS environment variables, application.properties, YAML files, and custom sources) in a well-defined precedence order.
The colon syntax — ${key:defaultValue} — supplies a fallback used when the key is absent. Without a default, a missing key throws a BeanCreationException at startup, which is exactly the behavior you want for mandatory settings. Use defaults only for genuinely optional knobs.
The corresponding application.properties:
application-{profile}.properties > application.properties > @PropertySource files > defaults. This means you can override any property at deployment time without touching the JAR — a key twelve-factor app principle.
SpEL Expressions Inside @Value
The #{...} syntax activates the Spring Expression Language, which goes beyond simple lookups. You can call methods, access other beans, perform arithmetic, and invoke static utilities:
SpEL is powerful, but keep it simple inside @Value. Complex logic belongs in a @Bean factory method or a service class, not in an annotation string that is hard to read and impossible to debug with a breakpoint.
Injecting Lists and Arrays
A comma-separated property value is automatically split into a List or array when the injection target is typed accordingly:
Spring's ConversionService handles the split and type coercion. For numeric lists (List<Integer>, int[]) the same technique works — each token is parsed to the target type.
Injecting Maps
Maps require the SpEL form because a flat key=value property cannot carry the map structure on its own:
The outer #{} evaluates the property value as a SpEL expression, and the ${} inside expands it first. This layering is intentional: ${locale.currency.map} expands to the literal string {en_US:'USD',...}, and then SpEL parses it as a map.
@ConfigurationProperties (see below). Map injection via SpEL is fragile — a typo in the property file produces a cryptic parse error at startup rather than a clear validation message.
Loading External Property Files with @PropertySource
Properties do not have to live in application.properties. You can point Spring at any classpath or filesystem resource:
ignoreResourceNotFound = true is useful for optional override files that only exist in certain environments (production secrets, developer-specific overrides). Without it, a missing file fails the context load.
Type-Safe Binding with @ConfigurationProperties
When a group of related settings belongs together — a database pool, an email server, a payment gateway — @ConfigurationProperties is the right tool. It binds a prefix of the property namespace to a plain Java object (a POJO), with full type conversion, validation, and IDE auto-completion support.
Notice mail.start-tls in the properties file maps to setStartTls in the class. Spring's Relaxed Binding normalises kebab-case, camelCase, underscores and uppercase — they are all equivalent for binding purposes. This is why MAIL_PASSWORD (an OS environment variable) can satisfy a property called mail.password.
@Validated and use Bean Validation constraints (@NotBlank, @Min, @Max, @Pattern, etc.) on the fields. Spring will refuse to start if the bound values violate the constraints — far better than discovering a misconfiguration at runtime.
@Value vs @ConfigurationProperties — The Decision Rule
- Use
@Valuefor one-off, isolated values — a single timeout, a feature flag, an API key used in exactly one bean. It is quick and requires no extra class. - Use
@ConfigurationPropertiesfor any cohesive group of three or more related settings. It is self-documenting, validates at startup, and generates IDE metadata that provides auto-complete inapplication.properties. - Never scatter the same prefix across a dozen
@Valueannotations in different beans. Centralise it in a single@ConfigurationPropertiesclass and inject that class where needed.
Summary
@Value with the ${key:default} syntax is the entry point for scalar and simple collection injection; the #{spel} form unlocks maps and computed values. @PropertySource lets you load additional property files. For groups of related settings, @ConfigurationProperties offers type safety, validation, and relaxed binding — and it should be your default choice for anything that constitutes a configuration namespace. Together, these mechanisms keep configuration out of your code and under the control of the deployment environment.