skip to content

Which BeanPostProcessors handle @Autowired, @Resource, and @Inject, and how do their qualifier/name semantics interact when multiple candidates exist?

level: principalimportance: nice to knowfreq 22%

answer

  1. AutowiredAnnotationBPP → @Autowired/@Inject/@Value
  2. CommonAnnotationBPP → @Resource/@PostConstruct
  3. QualifierAnnotationAutowireCandidateResolver reads @Qualifier & @Named
  4. qualifier filters, @Primary tiebreaks
  5. @Resource exact name beats @Primary

basics

~10 s

AutowiredAnnotationBeanPostProcessor handles @Autowired and @Inject (by type, with @Qualifier/@Named narrowing). CommonAnnotationBeanPostProcessor handles @Resource (by name first, type fallback). Both are registered by default in Spring.

solid answer

~40 s

Two post-processors do the work. AutowiredAnnotationBeanPostProcessor processes @Autowired, @Value, and (if present) JSR-330 @Inject — all by-type, then narrowing via @Qualifier or @Named (both treated as name qualifiers) and @Primary. CommonAnnotationBeanPostProcessor processes JSR-250 @Resource — by-name first (explicit name or derived field/property name), with a by-type fallback. When several beans of a type exist: @Autowired/@Inject need a qualifier or @Primary to disambiguate, otherwise NoUniqueBeanDefinitionException; @Resource sidesteps this by matching the name directly. @Primary influences by-type resolution but a qualifier or an exact @Resource name takes precedence over @Primary. Both processors are auto-registered via AnnotationConfigApplicationContext / <context:annotation-config>, so no manual wiring is normally needed.

go deeper

for a junior

Not expected to name the processors.

for a middle

Might name AutowiredAnnotationBeanPostProcessor and know @Resource is separate.

for a senior

Names both processors and the qualifier-vs-primary precedence.

for a principal

Articulates the full narrowing order, the candidate-resolver role, name-beats-primary for @Resource, and the design guidance to standardize one style.

### The processors Spring turns injection annotations into actual wiring using **BeanPostProcessors** registered during context bootstrap (by `AnnotationConfigApplicationContext`, Spring Boot auto-config, or `<context:annotation-config/>`): - **`AutowiredAnnotationBeanPostProcessor`** — handles `@Autowired`, `@Value`, and **`@Inject`** (`jakarta.inject.Inject`) when that API is on the classpath. All are **by-type** resolvers. - **`CommonAnnotationBeanPostProcessor`** — handles JSR-250 common annotations including **`@Resource`** (by-name-first), plus lifecycle annotations `@PostConstruct`/`@PreDestroy`. `@Resource` resolution: explicit `name`, else derived injection-point name, else **type fallback**. Qualifier interpretation is delegated to a `AutowireCandidateResolver` — specifically `QualifierAnnotationAutowireCandidateResolver`, which understands Spring's `@Qualifier` **and** JSR-330 `@Named` (because @Named is meta-annotated with the JSR-330 `@Qualifier`), matching candidates by qualifier value / bean name. ### Resolution precedence for by-type injection (@Autowired / @Inject) Given multiple candidates of the required type, Spring narrows in this order: 1. **Qualifier match** — a `@Qualifier("x")` / `@Named("x")` on the injection point restricts to the bean(s) whose qualifier/name is `x`. 2. **@Primary** — among remaining candidates, a bean marked `@Primary` wins. (A qualifier match narrows the set *before* @Primary is consulted, so an explicit qualifier effectively beats @Primary.) 3. **`@Priority`** (jakarta.annotation.Priority) — if still multiple, the lowest priority value wins. 4. **Fallback to injection-point name** — the field/parameter name is used as a default qualifier matching a bean id. 5. If none of these yields exactly one → `NoUniqueBeanDefinitionException`; if zero → `NoSuchBeanDefinitionException` (unless `required=false`/Optional/ObjectProvider). ### Resolution for @Resource (by-name) 1. Bean whose **name** equals the explicit `name` attribute, or the derived field/property name. 2. If no name match → **by-type** fallback (then subject to the same ambiguity rules as above — but note @Primary applies in this type-fallback stage, not in the name stage). Because @Resource matches an exact name, it **ignores @Primary** whenever the name resolves — the name is authoritative. ### Interaction subtleties - **@Primary vs @Qualifier:** @Qualifier/@Named is a *filter*; @Primary is a *tiebreaker*. An explicit qualifier that selects a non-primary bean wins over @Primary because the primary is filtered out of the candidate set. - **@Primary vs @Resource name:** an exact @Resource name wins; @Primary only matters if @Resource falls back to type. - **@Inject + @Named vs @Autowired + @Qualifier:** functionally equivalent; both route through the same candidate resolver. - **Ordering of processors:** they operate on different annotations, so on a given element only one applies; there's no conflict unless you redundantly annotate (e.g., both @Autowired and @Resource on one field — avoid; behavior is confusing and order-dependent). - **Classpath dependency:** @Inject/@Named/@Resource all need the relevant Jakarta APIs (`jakarta.inject-api`, `jakarta.annotation-api`) present, or the annotations are ignored. ### Why this matters at a design level Understanding which processor owns which annotation explains **why** @Resource can disambiguate without @Qualifier, **why** @Primary sometimes seems ignored (a qualifier or @Resource name overrode it), and **why** mixing annotation families in one codebase invites confusion. A principal engineer standardizes on one style (typically constructor `@Autowired` + `@Qualifier`/`@Primary`) and reserves @Resource/JSR-330 for deliberate portability or by-name needs. ### Gotchas - Redundant `<context:annotation-config/>` plus component scanning can register processors twice; Spring guards against double-registration, but hand-declaring these processors as beans can cause surprises. - `@Priority` uses `jakarta.annotation.Priority` and only breaks ties among by-type candidates, not by-name. - On collections/arrays (`List<Foo>`), @Autowired injects **all** matching beans (ordered by @Order/@Priority), which changes the 'ambiguity' story entirely.

  • If a field has @Autowired plus @Qualifier("a") and another bean of the same type is @Primary, which is injected?
    The bean named/qualified 'a'. @Qualifier filters candidates to just 'a', so @Primary never gets a chance to act as tiebreaker — the qualifier wins over @Primary.
  • Registers these post-processors automatically in a Spring Boot app?
    Yes. Spring Boot (and any AnnotationConfigApplicationContext, or <context:annotation-config/>) auto-registers AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor, so @Autowired/@Inject/@Resource work without manual bean declarations.
  • How does @Autowired behave differently when the target is List<Foo> instead of a single Foo?
    It injects all beans of type Foo as a collection (ordered by @Order/@Priority), so there is no ambiguity error — 'multiple candidates' is the expected, valid case for collection injection.

saying these in an interview costs you the question

  • Saying @Resource is handled by AutowiredAnnotationBeanPostProcessor
  • Claiming @Primary overrides an explicit @Qualifier
  • Thinking you must manually declare these BeanPostProcessors as beans
  • Believing @Named can't participate in the same candidate-resolution path as @Qualifier

context