@Primary selects a default candidate for a single-valued dependency; @Qualifier narrows a particular injection point.
Spring @Primary and @Qualifier: default selection versus an explicit bean
Name both decisions
A dispatch planner needs the regional route for normal requests and a fallback route for an explicit recovery path. Mark one gateway as primary for the unqualified constructor parameter, then qualify the fallback parameter by bean name. The checked context resolves those two different objects without asking the planner to search the container.
A qualifier is a selection constraint, not a fallback order. If the named bean is absent, context creation fails. Two primary beans of the same matching type are still ambiguous. Qualifier basics and constructor injection explain the underlying candidate resolution.
Keep configuration changes observable
Renaming a bean can break a string qualifier even though the Java type still compiles. A small context test catches that failure before startup reaches traffic. Prefer a semantic custom qualifier when multiple implementations will evolve independently; a default should be an intentional application policy, not an accidental registration order.
Checked code
@Bean @Primary DispatchGateway regionalGateway() { return () -> "regional"; }
@Bean DispatchGateway fallbackGateway() { return () -> "fallback"; }
@Bean DispatchPlanner dispatchPlanner(
DispatchGateway defaultGateway,
@Qualifier("fallbackGateway") DispatchGateway fallbackGateway) {
return new DispatchPlanner(defaultGateway, fallbackGateway);
}Verification boundary
The Maven source kit passes WiringAndBindingContractTest.primarySetsDefaultWhileQualifierSelectsAnExplicitGateway on its local Spring Boot 4 and Java 21 fixture.
Cost and ownership
Bean selection happens during context assembly, not for every dispatch call. The test proves one local object graph; it does not prove a network gateway works or that a fallback endpoint is healthy. If the planner keeps singleton collaborators, they must still be safe for concurrent requests.
Common Mistakes
- Do not use @Primary as a replacement for an explicit recovery dependency.
- Do not assume a qualifier will silently choose another bean when its target disappears.
- Do not infer thread safety from singleton bean selection.
Read next
Spring qualifiers: select a collaborator when types are ambiguous, Spring constructor injection: required dependencies stay visible, Spring Boot auto-configuration: conditions and user-defined beans, Spring @Component versus @Bean: choose who constructs the dependency.
