@Value

Injects a single configuration value or expression into a constructor parameter, field or @Bean method parameter.

Signature
@Value("${property.key:default}")  |  @Value("#{spel.expression}")
  • Added in Spring Boot Spring Framework 3.0

Parameters

ParameterTypeDescription
$valueStringThe expression: ${key} for a property, ${key:default} with a fallback, or #{...} for a SpEL expression.

Return value

n/aInjects the resolved value of a property placeholder or SpEL expression, converted to the type of the parameter or field.

Errors

A placeholder with no value and no default fails startup with "Could not resolve placeholder". A value that cannot be converted fails with a TypeMismatchException.

@Value injects one value from the environment: application.properties, application.yaml, environment variables, command-line arguments and the rest of Spring Boot's property sources. Inside the annotation, ${...} is a property placeholder and #{...} is a SpEL expression.

public InvoiceMailer(@Value("${app.mail.from}") String from,
                     @Value("${app.mail.batch-size:50}") int batchSize) { ... }

Defaults and missing values

A placeholder without a value fails startup with Could not resolve placeholder 'app.mail.from'. That is usually what you want: a missing setting is caught at deploy time, not on the first email. Add a default after a colon, ${app.mail.batch-size:50}, only when there is a genuinely sensible one. An empty default, ${app.storage.bucket:}, gives an empty string.

Values are converted to the target type: int, boolean, Duration, enums, and a comma-separated value to a List<String>. A value that cannot be converted, such as batch-size=fifty, fails startup.

Where it works

Use it on constructor parameters of a bean (the best place: the field can be final), on @Bean method parameters, or on fields. It does not work on static fields, and it does nothing on objects you create with new, because Spring never sees them. Reading a @Value field from the constructor is another trap: field injection happens after construction, so the field is still null there.

When to use @ConfigurationProperties instead

@Value is fine for one or two unrelated values. Once a class reads several settings with the same prefix, move to a @ConfigurationProperties record. You get one typed object, validation, IDE completion and one place to look, instead of key strings scattered across classes where a typo in one is only discovered at runtime.

Secrets

Never put a real secret as the default in the annotation; it ends up in source control and in the compiled class. Leave the default off so a missing secret fails startup, and supply it through an environment variable such as APP_PAYMENTS_API_KEY, which Spring Boot maps to app.payments.api-key.

Example

Example · java
import java.time.Duration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class InvoiceMailer {

    private final String from;
    private final int batchSize;
    private final Duration pause;

    public InvoiceMailer(@Value("${app.mail.from}") String from,
                         @Value("${app.mail.batch-size:50}") int batchSize,
                         @Value("${app.mail.pause:PT2S}") Duration pause) {
        this.from = from;
        this.batchSize = batchSize;
        this.pause = pause;
    }

    public String describe() {
        return "Sending as " + from + " in batches of " + batchSize + ", pausing " + pause;
    }
}

When to use it

  • Reading a sender address and batch size for a mailer
  • Supplying a connect timeout to a client built in a @Bean method
  • Injecting a short comma-separated list such as admin recipients

Frequently asked questions

Why is my @Value field null?

Either the object was created with new (Spring never injected it), the field is static, or you read it in the constructor before field injection ran. Move the @Value to a constructor parameter.

How do I give @Value a default?

Put it after a colon inside the placeholder: @Value("${app.page-size:20}"). Use ${key:} for an empty string default.

Should I use @Value or @ConfigurationProperties?

@Value for one or two isolated settings; @ConfigurationProperties when several settings share a prefix or need validation.

More examples

The matching configuration

app.* keys are your own, so Spring Boot does not validate them. In production any of these can be overridden by an environment variable, for example APP_MAIL_BATCH_SIZE=200.

Example · properties
# Required: startup fails if this is missing.
[email protected]

# Optional: override the defaults in the annotations.
app.mail.batch-size=100
app.mail.pause=PT5S

# Comma-separated values convert to List<String>.
[email protected],[email protected]

Lists and expressions

The comma-separated property becomes a List<String>; with the empty default it becomes an empty list. The SpEL expression is evaluated once at startup. Keep expressions this small: logic in annotation strings is not compiled or tested.

Example · java
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ReportSettings {

    private final List<String> adminCopies;
    private final int workers;

    public ReportSettings(@Value("${app.mail.admin-copies:}") List<String> adminCopies,
                          @Value("#{T(java.lang.Math).max(2, T(java.lang.Runtime).getRuntime().availableProcessors() / 2)}") int workers) {
        this.adminCopies = adminCopies;
        this.workers = workers;
    }

    public List<String> adminCopies() {
        return adminCopies;
    }

    public int workers() {
        return workers;
    }
}

@Value on a @Bean method parameter

A @Bean method parameter is resolved like a constructor parameter, so @Value works there too. This keeps the value next to the one place that uses it.

Example · java
import java.net.http.HttpClient;
import java.time.Duration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class HttpConfig {

    @Bean
    HttpClient httpClient(@Value("${app.http.connect-timeout:PT3S}") Duration connectTimeout) {
        return HttpClient.newBuilder()
                .connectTimeout(connectTimeout)
                .build();
    }
}

Discussion

  • Be the first to comment on this lesson.