@Qualifier
About
In Spring Framework, @Qualifier
is used to resolve ambiguity when multiple beans of the same type exist in the application context.
By default, Spring performs type-based dependency injection - if more than one bean matches the required type, Spring cannot decide which one to inject and will throw a NoUniqueBeanDefinitionException
.
The @Qualifier
annotation helps by explicitly specifying which bean should be injected, either by using the bean name or a custom qualifier value.
It is commonly used in combination with @Autowired
(or @Inject
) to refine the injection target.
Attributes
@Qualifier
has a single attribute:
value
String
Yes
Specifies the name (or qualifier) of the bean to inject. This must match the bean's name or the value provided in its own @Qualifier
or @Component
/@Bean
annotation.
Mandatory: The
value
attribute is always required; without it,@Qualifier
serves no purpose.Bean Matching:
If the bean is defined with
@Component("customName")
or@Bean(name="customName")
, thevalue
in@Qualifier
should match"customName"
.If the bean is defined with another
@Qualifier("name")
, thevalue
should match"name"
.
No Default Value: There’s no default - we must explicitly provide the bean name.
How It Works ?
@Qualifier
narrows the set of type-matching candidate beans so Spring can pick the right one when more than one bean implements the requested type. It is applied after Spring selects candidates by type and before falling back to defaults like @Primary
or name-based matching.
Resolution flow
This is what Spring does step by step:
Type-based candidate selection When an injection point is processed (field, constructor parameter, method parameter), Spring first finds all beans whose type matches the required type. This is the base set of candidates.
Apply qualifier filtering (if present at the injection point) If the injection point has a
@Qualifier("q")
(or a custom qualifier), Spring filters the type-matching candidates to those that have a matching qualifier value (or matching qualifier annotation metadata). Qualifier matching is performed within the already type-selected candidates, it isn’t a global name lookup. That’s why@Qualifier
is a narrowing/filtering mechanism rather than a direct “inject-by-id” command.Bean-name fallback and injection-point name matching If no explicit qualifier is present, Spring may consider the bean name (or the injection-point parameter/field name) as a fallback qualifier i.e., the field/parameter name can match a bean id/alias and resolve the ambiguity.
@Primary
as a fallback preference If qualifiers (and name matching) don’t resolve the ambiguity but exactly one candidate is annotated@Primary
, that primary bean will be selected.@Primary
provides a default when we don’t want to annotate every injection point.If the candidate set still contains multiple beans → failure If Spring still sees multiple equally-matching candidates after the above steps, it throws a
NoUniqueBeanDefinitionException
(we must disambiguate with@Qualifier
,@Primary
, changing the injection type to a collection, etc.).
Notes
Qualifiers narrow within type matches -
@Qualifier
doesn’t change the type-based search; it filters the beans that already match the required type. This is why giving semantic qualifier names (e.g."fast"
,"persistent"
) is recommended over relying on generated bean ids.Qualifier vs bean-name vs
@Primary
precedence@Qualifier
is the explicit narrowing mechanism and typically wins over defaults. If we put@Qualifier
at the injection point, Spring uses that filter first. If no qualifier is present,@Primary
can act as a tie-breaker. (In short: qualifier narrows first → primary is used if no qualifier and exactly one primary exists → then name matching can apply as a fallback.)
Qualifier values are not required to be unique A qualifier can be used as a filter for collections: if we inject
Set<MyType>
and annotate the injection with@Qualifier("k")
, all beans ofMyType
that carry qualifier"k"
will be injected into the collection. So qualifiers can select multiple beans, not just a single one.Where we can put qualifiers
@Qualifier
may be applied to fields, method/constructor parameters, and setter methods - use it wherever we narrow injection. For constructor/multi-arg methods, prefer qualifiers on the parameter level.
Best Practice
Using @Qualifier
correctly ensures that Spring injects the intended bean in a clean, maintainable way. Since qualifiers are often introduced when an application grows in complexity, their use should be strategic, not haphazard.
1. Use @Qualifier
for Explicit Disambiguation
@Qualifier
for Explicit DisambiguationOnly use @Qualifier
when we genuinely have multiple beans of the same type. Overuse can make the code verbose and harder to refactor. If there’s only one bean for a type, Spring can inject it automatically without qualifiers.
2. Match Qualifier Value to Bean Definition Intentionally
When using @Qualifier("beanName")
, ensure that beanName
matches how the bean is registered:
With
@Component("beanName")
With
@Bean(name="beanName")
With another
@Qualifier("beanName")
on the bean definition
Avoid relying on Spring’s default bean naming (which is typically the class name with a lowercase first letter) in production code - explicit naming is more robust against refactoring.
3. Prefer Semantic Qualifier Names Over Implementation Names
Instead of naming qualifiers after specific implementation classes (e.g., "fastPaymentService"
), prefer semantic names that describe the behavior or purpose, such as "fast"
, "reliable"
, or "cached"
. This decouples our injection logic from specific class names and supports easier refactoring.
4. Combine with @Primary
for Defaults
@Primary
for DefaultsWhen one bean should be the default and others are used only in special cases, annotate the default with @Primary
and apply @Qualifier
only at injection points that need the alternative. This avoids unnecessary qualifiers on most injection points.
5. Use Custom Qualifier Annotations for Clarity
For complex systems with multiple dimensions of selection, create custom qualifier annotations meta-annotated with @Qualifier
.
Example:
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface PaymentType {
String value();
}
This avoids magic strings and improves readability:
@Autowired
@PaymentType("fast")
private PaymentService service;
6. Consider Qualifier Use in Collections and Maps
When injecting List<T>
or Map<String, T>
, qualifiers can be used to filter which beans appear in the collection. This is more scalable than injecting single beans repeatedly.
7. Avoid Hidden Conflicts in Large Teams
In large projects with multiple developers, establish team-wide naming conventions for qualifiers. Without naming rules, duplicate or conflicting qualifier names can cause subtle injection errors that are hard to trace.
8. Test Bean Resolution Explicitly
When writing integration tests, include tests that verify Spring’s ability to resolve the intended bean with the given qualifiers. This guards against accidental renaming or qualifier removal.
9. Avoid Overlapping Qualifiers
If a bean is annotated with multiple qualifiers and another bean shares one of them, we can unintentionally make the resolution ambiguous again. Keep qualifiers orthogonal - one qualifier should clearly distinguish a bean without overlap unless intentionally grouping.
Example
1. Basic Disambiguation with Multiple Beans
When multiple beans of the same type exist, @Qualifier
specifies which one to inject.
@Component("fastPaymentService")
class FastPaymentService implements PaymentService { }
@Component("slowPaymentService")
class SlowPaymentService implements PaymentService { }
@Component
class PaymentController {
@Autowired
@Qualifier("fastPaymentService")
private PaymentService paymentService;
}
Here, fastPaymentService
is injected because the qualifier matches the bean name.
2. Using @Qualifier
with Constructor Injection
@Qualifier
with Constructor Injection@Component
class PaymentController {
private final PaymentService paymentService;
@Autowired
public PaymentController(@Qualifier("slowPaymentService") PaymentService paymentService) {
this.paymentService = paymentService;
}
}
@Qualifier
can be applied directly to constructor parameters to select a specific bean.
3. Applying @Qualifier
on Setter Injection
@Qualifier
on Setter Injection@Component
class PaymentController {
private PaymentService paymentService;
@Autowired
public void setPaymentService(@Qualifier("fastPaymentService") PaymentService paymentService) {
this.paymentService = paymentService;
}
}
Setter-based injection works the same way as field and constructor injection.
4. Defining Qualifier at Bean Creation with @Bean
@Bean
@Configuration
class AppConfig {
@Bean
@Qualifier("fast")
public PaymentService fastPaymentService() {
return new FastPaymentService();
}
@Bean
@Qualifier("slow")
public PaymentService slowPaymentService() {
return new SlowPaymentService();
}
}
The @Qualifier
values here are used when injecting beans later.
5. Matching Bean Name Without Explicit Qualifier
If a bean is named with @Component("fast")
, we can match it with @Qualifier("fast")
without adding a qualifier annotation at the bean definition level.
@Component("fast")
class FastPaymentService implements PaymentService { }
@Component
class PaymentController {
@Autowired
@Qualifier("fast")
private PaymentService paymentService;
}
6. Combining @Primary
and @Qualifier
@Primary
and @Qualifier
@Component
@Primary
class DefaultPaymentService implements PaymentService { }
@Component
class SpecialPaymentService implements PaymentService { }
@Component
class PaymentController {
@Autowired
private PaymentService defaultService; // Injects DefaultPaymentService
@Autowired
@Qualifier("specialPaymentService")
private PaymentService specialService; // Explicitly selects the special one
}
@Primary
defines the default, while @Qualifier
picks a non-default option.
7. Custom Qualifier Annotation
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface PaymentType {
String value();
}
@Component
@PaymentType("fast")
class FastPaymentService implements PaymentService { }
@Component
@PaymentType("slow")
class SlowPaymentService implements PaymentService { }
@Component
class PaymentController {
@Autowired
@PaymentType("fast")
private PaymentService paymentService;
}
This avoids magic strings and makes code self-documenting.
8. Filtering Beans in Collections
@Component
@Qualifier("fast")
class FastPaymentService implements PaymentService { }
@Component
@Qualifier("slow")
class SlowPaymentService implements PaymentService { }
@Component
class PaymentProcessor {
@Autowired
@Qualifier("fast")
private List<PaymentService> fastServices; // All beans with qualifier "fast"
}
@Qualifier
can select multiple beans into a collection.
9. Using @Qualifier
with @Bean(name=...)
@Qualifier
with @Bean(name=...)
@Configuration
class PaymentConfig {
@Bean(name = "fastService")
public PaymentService fastPaymentService() {
return new FastPaymentService();
}
@Bean(name = "slowService")
public PaymentService slowPaymentService() {
return new SlowPaymentService();
}
}
@Component
class PaymentController {
@Autowired
@Qualifier("fastService")
private PaymentService paymentService;
}
10. Multiple Attributes in Custom Qualifiers
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface PaymentQualifier {
String speed();
String region();
}
@Component
@PaymentQualifier(speed = "fast", region = "US")
class FastUSPaymentService implements PaymentService { }
@Component
class PaymentController {
@Autowired
@PaymentQualifier(speed = "fast", region = "US")
private PaymentService paymentService;
}
Spring will match all specified attributes exactly.
Last updated