@Value Injection
The @Value annotation injects a single externalized property directly into a field, constructor parameter, or method argument. It is the quickest way to pull one configuration value out of application.yml, the environment, or a command-line argument, and it supports placeholders, defaults, and inline SpEL expressions.
Injecting a single property
Use placeholder syntax ${...} to reference a property key. Spring resolves it from the Environment at bean-creation time.
# application.yml
app:
name: Devcraftly Shop
max-connections: 50
@Service
public class GreetingService {
@Value("${app.name}")
private String appName;
@Value("${app.max-connections}")
private int maxConnections;
public String banner() {
return appName + " (pool=" + maxConnections + ")";
}
}
Spring converts the raw string to the target type automatically: int, long, boolean, Duration, DataSize, enums, and more.
Tip: Prefer constructor injection over field injection so the value is available immediately and the class stays testable.
@Service
public class GreetingService {
private final String appName;
public GreetingService(@Value("${app.name}") String appName) {
this.appName = appName;
}
}
Default values
If a property might be missing, supply a fallback after a colon. Without a default, an unresolved placeholder throws IllegalArgumentException at startup.
@Value("${app.timeout:30}") // 30 if app.timeout is absent
private int timeoutSeconds;
@Value("${app.greeting:Hello there}") // string default with spaces
private String greeting;
@Value("${app.feature.beta:false}") // boolean default
private boolean betaEnabled;
An empty default produces an empty string:
@Value("${app.optional-suffix:}")
private String suffix; // "" when not set
Reading environment variables
Spring’s relaxed binding maps environment variables to property keys: uppercase the key and replace dots and dashes with underscores. So app.name is satisfied by an APP_NAME variable.
export APP_NAME="Prod Shop"
java -jar app.jar
You can also read an OS variable directly by name, which is handy for standard variables like HOME or PATH:
@Value("${HOME}")
private String homeDir;
@Value("${REGION:us-east-1}") // default if REGION is unset
private String region;
Note: Reading variables via the property name (
app.name) is preferred — it lets the value also come from YAML, command-line args, or a config server, not just the OS environment.
SpEL expressions
The #{...} syntax evaluates a Spring Expression Language expression instead of a property placeholder. Use it for computed values, bean references, and conditional logic. See the dedicated SpEL page for the full grammar.
// Literal expression
@Value("#{2 * 60 * 1000}")
private long defaultDelayMs; // 120000
// Read another bean's property and transform it
@Value("#{greetingService.appName.toUpperCase()}")
private String upperName;
// System properties / environment via SpEL
@Value("#{systemProperties['user.timezone'] ?: 'UTC'}")
private String timezone;
// Combine a property placeholder inside a SpEL expression
@Value("#{${app.max-connections} * 2}")
private int burstLimit;
| Syntax | Meaning | Example |
|---|---|---|
${...} | Property placeholder | ${app.name} |
${key:default} | Placeholder with fallback | ${app.timeout:30} |
#{...} | SpEL expression | #{T(java.lang.Math).PI} |
#{${...}} | SpEL over a resolved property | #{${app.max} * 2} |
Lists and arrays
A comma-separated property binds to a String[], List<String>, or Set<String>. Split it with SpEL when you need a typed collection.
app:
servers: alpha,beta,gamma
ports: 8080,8081,8082
// Array — Spring splits on commas automatically
@Value("${app.servers}")
private String[] servers;
// List via SpEL split()
@Value("#{'${app.servers}'.split(',')}")
private List<String> serverList;
// Numeric list with a default when the key is missing
@Value("#{'${app.ports:9090}'.split(',')}")
private List<Integer> ports;
For maps and nested structures, @Value is the wrong tool — switch to typed binding.
Maps with SpEL
You can inject a map literal or parse one, but this gets unwieldy quickly:
@Value("#{${app.limits:{free:10, pro:100}}}")
private Map<String, Integer> limits;
Warning: Once you find yourself splitting strings or parsing maps in
@Value, you have outgrown it. Use @ConfigurationProperties for type-safe, validated, IDE-completable binding of grouped settings.
@Value vs @ConfigurationProperties
| Aspect | @Value | @ConfigurationProperties |
|---|---|---|
| Best for | One or two scalar values | Groups of related settings |
| Type safety | Per field | Whole object, validated |
| Relaxed binding | Limited | Full (max-attempts → maxAttempts) |
| SpEL support | Yes | No |
| Validation | No (manual) | Yes via @Validated |
| IDE metadata | No | Yes (with annotation processor) |
Common pitfalls
- Missing property, no default: the context fails to start with
Could not resolve placeholder. Add a default or define the key. - Wrong syntax:
${}is a placeholder,#{}is SpEL — mixing them silently changes behavior. - Field injection in tests: prefer constructor injection so you can pass values directly without the Spring context.
- Static fields:
@Valuedoes not work onstaticfields; Spring only injects instance members.
Best Practices
- Reserve
@Valuefor one-off scalar values; group settings belong in @ConfigurationProperties. - Always provide a sensible default for optional properties so the app starts cleanly.
- Reference properties by key (
${app.name}) rather than raw env names so values stay portable across sources. - Use constructor injection to keep beans immutable and unit-testable.
- Keep SpEL expressions short; move real logic into a bean method instead.