What is the difference between @Value("${...}") and @Value("#{...}"), and can they be combined?
answer
- ${} = property lookup, #{} = SpEL evaluation
- Placeholders resolved BEFORE SpEL
- Nest: #{'${prop}'.split(',')}
- #{} can reference beans, ${} cannot
- #{app.x} means bean app, not property
basics
~20 s${...} is a property placeholder: it looks up a property in the Environment. #{...} is a SpEL expression that is evaluated (method calls, math, bean references). You can nest a placeholder inside SpEL, e.g. #{'${my.list}'.split(',')}.
solid answer
~40 s`${...}` and `#{...}` are two different mechanisms. `${prop}` is property-placeholder resolution handled by PropertySourcesPlaceholderConfigurer — it substitutes a property value from the Environment as text. `#{expr}` is a Spring Expression Language evaluation: it can call methods, do arithmetic, reference other beans (`#{otherBean.prop}`), and return typed objects. Placeholders are resolved first (they are simple text replacement), then SpEL is evaluated. That ordering lets you nest a placeholder inside an expression: `@Value("#{'${app.list}'.split(',')}")` reads the property text, then splits it into an array via SpEL. You cannot do the reverse — SpEL result feeding a placeholder. Use `${}` for plain configuration lookups and `#{}` only when you genuinely need computation or a bean reference; overusing SpEL hurts readability.
code
java · 18 lines@Component
public class FeatureConfig {
// Plain property lookup
@Value("${feature.name}")
private String name;
// SpEL: placeholder resolved first, then split into a List<String>
@Value("#{'${feature.roles}'.split(',')}")
private List<String> roles;
// SpEL arithmetic / comparison -> boolean
@Value("#{${feature.retries:3} > 0}")
private boolean retriesEnabled;
// SpEL referencing the Environment bean
@Value("#{environment['feature.region']}")
private String region;
}go deeper
Know ${} is a property and #{} is an expression; not much more.
Explain the two engines, the resolve-order (placeholder then SpEL), and the split-a-list idiom.
Discuss bean/environment references via SpEL, typed results, and readability/testability tradeoffs of SpEL in @Value.
Advise limiting SpEL in configuration, prefer typed binding, and understand that SpEL grammar/security lives in a separate concern from @Value dispatch.
**Two syntaxes, two engines.** - `${...}` — *property placeholder*. Resolved by `PropertySourcesPlaceholderConfigurer` (a `BeanFactoryPostProcessor`). It is essentially string substitution: find the property in the `Environment`, replace the placeholder with its value. Supports `${prop:default}`. - `#{...}` — *SpEL (Spring Expression Language)* expression. Evaluated by a `BeanExpressionResolver` (`StandardBeanExpressionResolver`). It parses and executes an expression: literals, operators (`+ - * /`, `?:`, ternary), method/property access, collection/map literals, and references to other beans by name. **Resolution order.** Placeholders are resolved *before* SpEL. Conceptually Spring first replaces every `${...}`, producing a final string, then if that string contains `#{...}` it evaluates the expression. This is why `#{'${app.roles}'.split(',')}` works: the inner `${app.roles}` becomes e.g. `ADMIN,USER`, giving SpEL the literal `'ADMIN,USER'`, on which `.split(',')` runs to produce `String[]`. **Referencing other beans.** SpEL can pull from beans: `@Value("#{systemProperties['user.region']}")`, `@Value("#{environment['app.name']}")`, or `@Value("#{myConfigBean.maxItems}")`. `${...}` cannot call methods or reach beans. **Typed results.** SpEL naturally yields typed objects (a `List`, an `int`, a `boolean` from a comparison like `#{app.max > 100}`). `${...}` yields text that is then converted by the `ConversionService`. **Common mistakes.** - Using `#{app.timeout}` expecting a property — that is SpEL trying to resolve a bean named `app`, not a property; the property form is `${app.timeout}`. - Trying to nest the other way, `${#{...}}` — unsupported. - Overusing SpEL for logic that belongs in code, hurting testability. **When to use each.** Default to `${...}` for configuration. Reach for `#{...}` only when you must compute (split a list, apply a default via a bean, do arithmetic, or reference another bean/environment programmatically). SpEL grammar internals are covered by the dedicated SpEL topic; here the point is only how `@Value` dispatches to each engine.
- Why does @Value("#{feature.name}") fail while @Value("${feature.name}") works?#{...} is SpEL, so feature is treated as a bean name and .name as a property access; there is no bean named 'feature', causing an evaluation error. The property form must use ${...}.
- In #{'${app.roles}'.split(',')}, which part runs first and why?The placeholder ${app.roles} is resolved first to its text value; SpEL then evaluates split(',') on that literal. Placeholder resolution always precedes SpEL evaluation.
saying these in an interview costs you the question
- Claiming ${} and #{} are interchangeable
- Saying SpEL is evaluated before placeholder resolution
- Thinking #{app.timeout} reads a property named app.timeout
- Believing you can nest ${#{...}}