How does @Qualifier select a specific bean, and what is bean-name fallback matching?
answer
- @Qualifier value: declared qualifier OR bean name
- field/param name = separate fallback
- name matching is fragile — prefer explicit
- qualifier filters before @Primary
- @Bean method name -> bean id via name fallback
basics
~20 s@Qualifier("name") tells Spring exactly which bean to inject when several match by type. If no bean carries that qualifier value, Spring falls back to matching the value against bean names — so @Qualifier can also target a bean by its id.
solid answer
~40 s@Qualifier narrows autowiring when type alone is ambiguous. Its value is matched against a bean's declared qualifier (a @Qualifier on the bean) and, as a fallback, against the bean's name/id. So @Qualifier("reporting") matches a bean explicitly qualified "reporting", or failing that a bean literally named "reporting". Separately, even without @Qualifier, Spring uses the injection point's field or parameter name as a fallback: an @Autowired DataSource named reportingDataSource will match a bean whose id is reportingDataSource. That name-based matching only kicks in after type match yields multiple candidates and no @Primary/@Qualifier settled it. Best practice: prefer explicit @Qualifier over relying on parameter-name matching, because the latter silently breaks if names drift or if you compile without parameter-name metadata.
code
java · 20 lines@Configuration
class DataConfig {
@Bean @Primary DataSource mainDataSource() { /* ... */ }
@Bean @Qualifier("reporting") DataSource reportingDataSource() { /* ... */ }
}
@Service
class Reports {
private final DataSource ds;
// Matches the bean declaring @Qualifier("reporting")
Reports(@Qualifier("reporting") DataSource ds) { this.ds = ds; }
}
@Service
class OtherReports {
private final DataSource ds;
// No declared qualifier 'reportingDataSource' exists, so this matches
// the bean by NAME (id = reportingDataSource) via the name fallback.
OtherReports(@Qualifier("reportingDataSource") DataSource ds) { this.ds = ds; }
}go deeper
Knows @Qualifier("x") picks a specific bean when several match.
Understands the value matches a declared qualifier OR a bean name, plus the separate field/param-name fallback.
Places qualifier filtering correctly in the resolution order and warns against fragile name matching.
Weighs string qualifiers vs custom qualifier annotations for large codebases and refactor safety.
## @Qualifier: explicit narrowing `@Qualifier` (`org.springframework.beans.factory.annotation.Qualifier`) is applied at the **injection point** to say *which* of several type-compatible beans you want. Its string value is resolved in two ways: 1. **Against a declared qualifier** — a bean can itself carry `@Qualifier("...")` (on the class or `@Bean` method). `@Qualifier("reporting")` at the injection point matches a bean annotated `@Qualifier("reporting")`. 2. **Against the bean name/id (fallback)** — if no bean declares that qualifier value, Spring treats the value as a **bean name**. So `@Qualifier("reportingDataSource")` will match the bean whose id is `reportingDataSource` even if that bean has no `@Qualifier` of its own. This dual behavior is why `@Qualifier("someBeanName")` "just works" for beans you never explicitly tagged. ## Bean-name fallback matching (without any @Qualifier) There's a second, distinct fallback: the **injection point's own name**. After a by-type lookup returns multiple candidates, and neither `@Primary` nor `@Qualifier` resolves it, Spring compares the **field name** or **constructor/method parameter name** to the candidate bean names. A match uniquely resolves the injection. ```java @Configuration class Config { @Bean DataSource mainDataSource() { ... } @Bean DataSource reportingDataSource() { ... } } @Service class ReportService { // parameter named 'reportingDataSource' -> matches that bean by name ReportService(DataSource reportingDataSource) { ... } } ``` **Caveat:** parameter-name matching depends on parameter names surviving compilation. With modern Spring Boot builds this is handled (Spring uses `-parameters` and/or the `-g` debug info / its own `ParameterNameDiscoverer`), but relying on it is fragile — a rename of the parameter silently changes wiring, with no compile error. Prefer an explicit `@Qualifier`. ## Resolution ordering (the part people miss) For a single-value injection with multiple type matches, Spring: 1. Filters candidates by any `@Qualifier` present. 2. If still multiple, applies `@Primary`. 3. If still multiple, applies `jakarta.annotation.Priority` (`@Priority`). 4. Finally falls back to matching the injection point name against candidate bean names. 5. If nothing uniquely resolves, throws `NoUniqueBeanDefinitionException`. (`@Qualifier` filtering happens up front, which is why a qualifier overrides `@Primary`.) ## Gotchas - **@Qualifier value is not the same as @Bean method name unless it falls through to name matching.** A `@Bean` method's name becomes the bean id; `@Qualifier("methodName")` hits it via the name fallback, not via a declared qualifier. - **On constructors with one parameter of a type**, you can put `@Qualifier` right on the parameter. Field/setter injection puts it next to the field/setter. - **@Qualifier can also be used on the bean side alone** to group beans; combined with a `@Qualifier` at the injection point of the same value, they match. - **Custom qualifier annotations** (meta-annotated with `@Qualifier`) give you type-safe, refactor-friendly alternatives to string values — covered separately.
- If @Qualifier("foo") matches neither a declared qualifier nor a bean named foo, what happens?No candidate satisfies the qualifier, so Spring throws NoSuchBeanDefinitionException (or NoUniqueBeanDefinitionException phrasing about no matching qualified bean) at startup — it fails fast rather than falling back to @Primary.
- Why is relying on parameter-name matching risky?It depends on parameter names being retained at compile time and silently rewires when someone renames the parameter — no compile error, just different injection. An explicit @Qualifier is refactor-safe.
saying these in an interview costs you the question
- Claiming @Qualifier can only reference a declared @Qualifier value and never a bean name
- Believing @Primary is consulted before @Qualifier filtering
- Assuming parameter-name matching is guaranteed regardless of compiler flags