What happens when @Value is placed on an injection point (or alongside @Autowired), and how does that interact with by-type autowiring?
answer
- same processor, different path
- getSuggestedValue checked FIRST
- @Value short-circuits by-type lookup
- @Value+@Autowired same field = @Value wins
- no required attr; use ${k:def}
basics
~20 s@Value tells Spring to inject a resolved value (from a property or expression) instead of looking up a bean by type. It's handled by the same processor as @Autowired, and when @Value supplies a value that value wins — no by-type bean lookup occurs for that point.
solid answer
~40 s@Value and @Autowired are both processed by AutowiredAnnotationBeanPostProcessor, but they resolve differently. During dependency resolution, DefaultListableBeanFactory first calls the AutowireCandidateResolver's getSuggestedValue on the DependencyDescriptor. @Value provides such a suggested value (its string, e.g. a ${...} placeholder or #{...} expression), so Spring resolves that string via the embedded value resolver and TypeConverter and injects the converted result — the by-type candidate search is skipped entirely. So on a single injection point, @Value short-circuits autowiring. Practically you mix them per parameter in a constructor: some parameters are beans (autowired by type), others carry @Value for config values. Putting both @Value and @Autowired on the same field is redundant — @Value's suggested value takes precedence and the bean lookup never runs. The detailed ${}/#{} placeholder and SpEL resolution mechanics belong to the Environment/SpEL leaf.
code
java · 15 lines@Service
class ReportService {
private final StorageClient storage; // autowired by type
private final int retentionDays; // injected from a property value
ReportService(StorageClient storage,
@Value("${reports.retention-days:30}") int retentionDays) {
this.storage = storage; // findAutowireCandidates -> the StorageClient bean
this.retentionDays = retentionDays; // getSuggestedValue -> resolve ${...} -> convert to int
}
// Anti-pattern: @Value wins, the @Autowired is dead weight, no bean lookup happens.
// @Value("${x}") @Autowired String confusing;
}go deeper
Know @Value injects a config value, @Autowired injects a bean.
Explain mixing @Value and @Autowired parameters in one constructor.
State that @Value short-circuits by-type resolution and give the default-value/placeholder gotchas.
Trace getSuggestedValue preceding findAutowireCandidates and reason about why config and bean injection never collide.
### Same processor, different resolution `@Value` (`org.springframework.beans.factory.annotation.Value`) and `@Autowired` are **both** handled by **`AutowiredAnnotationBeanPostProcessor`**. The difference is *how* the value is produced. ### The short-circuit mechanism Inside `DefaultListableBeanFactory.doResolveDependency`, the **first** thing checked is: ```java Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); ``` For a `@Value`-annotated point, `QualifierAnnotationAutowireCandidateResolver.getSuggestedValue` returns the annotation's **string** (e.g. `"${app.timeout:30}"` or `"#{systemProperties['user.region']}"`). Spring then: 1. resolves embedded `${...}` placeholders via the `StringValueResolver` bound to the `Environment`/`PropertySources`, 2. evaluates any `#{...}` **SpEL** expression, and 3. converts the result to the target type with the `TypeConverter`/`ConversionService`. Because a suggested value is present, **`findAutowireCandidates` is never called** — there is **no by-type bean lookup** for that injection point. That is the core interaction: **@Value wins over autowiring on the same point.** ### Mixing per parameter The common, idiomatic pattern is a constructor whose parameters mix bean injection and config values: ```java OrderService(PricingClient client, // by type @Value("${orders.max-items:100}") int maxItems) // by value ``` Each parameter is resolved independently — a `DependencyDescriptor` per parameter — so some are autowired beans and others are `@Value` conversions. ### @Value + @Autowired on the same field Redundant and slightly confusing: since `getSuggestedValue` fires first, the `@Value` value is used and the `@Autowired` by-type resolution is effectively ignored for that field. Don't do it; pick one intent per point. ### Edge cases & gotchas - **Type conversion failures**: `@Value("${port}")` into an `int` throws if the property isn't a number — a conversion error, not a bean error. - **Missing placeholder**: `@Value("${missing}")` with no default and no property throws `IllegalArgumentException` ("Could not resolve placeholder") at injection time; supply a default `${missing:fallback}`. - **Not a bean lookup**: you cannot use `@Value("#{someBean}")` expecting the same semantics as `@Autowired SomeBean` — although SpEL *can* reference beans by id, that is expression evaluation, not autowire candidate resolution. - **required**: `@Value` doesn't have a required attribute; provide defaults instead. - **Records / constructor binding**: `@Value` on record components / constructor params works the same way through the descriptor. ### Why this matters at a design level Understanding that `getSuggestedValue` precedes candidate search explains why config injection and bean injection never collide, why a stray `@Value` silently disables autowiring, and lets you predict resolution order precisely. The **placeholder/SpEL syntax and `Environment` layering** are detailed in the SpEL/Environment leaf — here the focus is the **interaction with @Autowired resolution**.
- If a field has both @Value and @Autowired, which one takes effect?@Value. Spring checks getSuggestedValue on the descriptor before any candidate search, so the resolved @Value value is injected and the by-type autowiring never runs. It's redundant — express one intent per injection point.
- Why does @Value never trigger a NoUniqueBeanDefinitionException?Because @Value supplies a suggested value up front, findAutowireCandidates is skipped entirely — there's no candidate set to be ambiguous. Failures instead come from unresolvable placeholders or type-conversion errors.
saying these in an interview costs you the question
- Thinking @Value looks up a bean by type like @Autowired
- Believing @Autowired overrides @Value on the same field
- Assuming a missing ${property} silently injects null instead of failing
- Expecting @Value to throw NoUniqueBeanDefinitionException