Skip to content
Spring Boot sb config 4 min read

@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;
SyntaxMeaningExample
${...}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 forOne or two scalar valuesGroups of related settings
Type safetyPer fieldWhole object, validated
Relaxed bindingLimitedFull (max-attemptsmaxAttempts)
SpEL supportYesNo
ValidationNo (manual)Yes via @Validated
IDE metadataNoYes (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: @Value does not work on static fields; Spring only injects instance members.

Best Practices

  • Reserve @Value for 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.
Last updated June 13, 2026
Was this helpful?