Key Takeaways
- An injection-point
@Qualifiernarrows the beans eligible for that dependency; it doesn't change the application's default everywhere. @Primarysupplies a preferred candidate for a single-valued dependency, including when a qualifier still leaves several eligible beans.- Put the qualifier on a constructor parameter for required dependencies. A component with one constructor doesn't need
@Autowiredon that constructor. - Bean names can participate in qualifier matching and injection-point name fallback, but those are different mechanisms.
Spring's @Qualifier selects among beans that match a dependency's type. This Spring @Qualifier example injects Pig into Farm even though Cow also implements Animal; @Primary provides a default when the remaining candidates need a preference.
The annotation belongs to Spring Framework, not specifically Spring Boot. If dependency injection is new to you, start with our guide to Spring @Autowired, then use the examples below to make bean selection explicit.
What Is the Use of @Qualifier in Spring?
A bean is an object managed by Spring's application context. An injection point is the place that requests a dependency, such as a constructor parameter.
For an ordinary, required dependency on one object, use this mental model:
- Match the type: an
Animaldependency can accept eitherCoworPig. - Apply qualifier requirements: an injection-point qualifier filters those candidates using their qualifier metadata or a matching bean name.
- Resolve any remaining choice: a unique eligible candidate can be injected directly; otherwise, preferences such as
@Primarymay select one.
This is a conceptual model, not the complete candidate-resolution algorithm. Injection-point names and other resolution indicators also matter in applicable cases. The important distinction is that qualifiers filter eligibility, while a primary marker expresses preference among eligible candidates.
Spring @Qualifier Example with Cow, Pig, and Animal
Set Up the Spring Boot Project
Generate a Maven Java project with Spring Initializr. Select a stable Spring Boot release, a compatible Java version, and the package name com.example.demo. Keep the generated Maven build and replace its application class with the one below.
This example needs the core Spring Boot starter, not a web or database starter. Ensure the following dependency appears inside your generated POM's dependencies element; don't add it twice:
<!-- Fragment inside pom.xml's dependencies element. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>Place all five Java files below in src/main/java/com/example/demo/. For a broader introduction to project structure and startup, see our Spring Boot tutorial.
Animal.java: Define the Shared Type
package com.example.demo;
public interface Animal {
void makeNoise();
}Cow.java: Register the First Implementation
package com.example.demo;
import org.springframework.stereotype.Component;
@Component("cow")
public class Cow implements Animal {
@Override
public void makeNoise() {
System.out.println("Moo");
}
}Pig.java: Register the Competing Implementation
package com.example.demo;
import org.springframework.stereotype.Component;
@Component("pig")
public class Pig implements Animal {
@Override
public void makeNoise() {
System.out.println("Oink");
}
}The explicit component names are cow and pig. With Spring's default component-name generator, these particular classes would receive the same names even without explicit values.
Farm.java: Select Pig with Constructor Injection
package com.example.demo;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class Farm {
private final Animal animal;
public Farm(@Qualifier("pig") Animal animal) {
this.animal = animal;
}
public void makeNoise() {
animal.makeNoise();
}
}The constructor requests an Animal qualified by pig. Here, that value matches the bean name registered by @Component("pig"); a separate qualifier annotation on Pig isn't necessary.
Constructor injection makes the required dependency visible and allows the field to be final. Because Farm has only one constructor, Spring uses it without @Autowired. The qualifier belongs on the parameter Spring is resolving, not merely on the backing field.
DemoApplication.java: Exercise the Injected Dependency
package com.example.demo;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
CommandLineRunner runFarm(Farm farm) {
return args -> farm.makeNoise();
}
}Spring Boot invokes the CommandLineRunner during startup, before SpringApplication.run(...) completes. It calls the farm's dependency rather than merely proving that the context can start.
From the project directory, use the Spring Boot Maven plugin's run goal:
mvn spring-boot:runAlongside Spring Boot's startup messages, the example prints:
OinkRemove the Qualifier to See the Ambiguity
Replace only the Farm constructor with this fragment:
// Fragment inside Farm.java; the other members stay unchanged.
public Farm(Animal animal) {
this.animal = animal;
}In this exact configuration, startup fails: both beans match Animal, neither is primary, and the parameter name animal matches neither bean name. The dependency failure is reported through UnsatisfiedDependencyException, with NoUniqueBeanDefinitionException identifying the ambiguous choice. Exception wording can vary; the meaningful problem is two eligible candidates for one dependency.
Bean Names and Qualifier Metadata Are Different
A bean name identifies a registered object. Qualifier metadata describes which kinds of dependencies that object should satisfy. A string qualifier can match a bean name, as above, but it can also express a role independent of the implementation's name.
Alternate Approach: Put Qualifier Metadata on the Beans
For example, keep the existing implementations but change their annotations:
// Fragment from Cow.java; existing imports and implementation omitted.
// Add: import org.springframework.beans.factory.annotation.Qualifier;
@Component("cow")
@Qualifier("pasture")
public class Cow implements Animal {
// ... existing makeNoise() implementation
}// Fragment from Pig.java; existing imports and implementation omitted.
// Add: import org.springframework.beans.factory.annotation.Qualifier;
@Component("pig")
@Qualifier("barn")
public class Pig implements Animal {
// ... existing makeNoise() implementation
}Now request the role at the injection point:
// Fragment inside Farm.java; other members stay unchanged.
public Farm(@Qualifier("barn") Animal animal) {
this.animal = animal;
}This selects the bean named pig because it carries matching barn metadata. Adding qualifiers only to the candidate classes doesn't tell an unqualified consumer which role it wants; the consumer must express that requirement. Spring also supports qualifier metadata on @Bean factory methods.
Spring @Qualifier vs @Primary
Use @Primary when most consumers should receive one default implementation. Use an injection-point @Qualifier when a particular consumer needs a specific role or candidate. A unique primary bean is preferred among the candidates eligible for a single-valued dependency.
To make Pig the default, add @Primary to its original component declaration:
// Fragment from Pig.java; existing imports and implementation omitted.
// Add: import org.springframework.context.annotation.Primary;
@Component("pig")
@Primary
public class Pig implements Animal {
// ... existing makeNoise() implementation
}With that change, the unqualified Farm(Animal animal) constructor receives Pig. An explicit @Qualifier("cow") on that parameter instead selects Cow, because the primary pig isn't eligible for that qualified dependency.
| Injection-point requirement | Candidate configuration | Result |
|---|---|---|
| Unqualified Animal animal | cow and pig; neither primary | Ambiguous in this example |
| Unqualified Animal animal | pig is the only primary | Pig |
| @Qualifier("cow") Animal animal | pig is primary | Cow |
| @Qualifier("livestock") Animal animal | Both carry livestock metadata; only pig is primary | Pig |
| Unqualified Animal animal | Both are primary in the same application context | NoUniqueBeanDefinitionException |
The last two rows explain why “qualifier always wins” is incomplete. A qualifier can leave several candidates, so a unique primary can still resolve the choice; two primary candidates in the same context don't establish a usable default. If a qualifier excludes one of those primaries and leaves one candidate, that dependency can still resolve.
What Happens When Qualifier Matching Fails?
- No candidates match: a required
Animalparameter with@Qualifier("sheep")fails because neither example bean qualifies. The underlying missing-bean error isNoSuchBeanDefinitionException. - Several candidates match: Spring still needs a unique selection for one
Animal. If no applicable preference or other resolution rule selects one, the result isNoUniqueBeanDefinitionException.
An unrelated primary bean doesn't rescue a qualifier that matches nothing. Read the underlying missing-bean or ambiguity error before changing annotations.
Shared qualifiers are useful when you intentionally request a group. For example, an injection point declared as @Qualifier("livestock") List<Animal> receives the matching group rather than requiring one winner. @Primary doesn't reduce a collection to its primary member.
Create a Custom Qualifier Annotation
When a domain distinction appears throughout your application, a custom annotation makes that vocabulary explicit. Spring recognizes annotations meta-annotated with @Qualifier; a meta-annotation is simply an annotation placed on another annotation.
For a farm organized by region, define Region.java in the same package:
package com.example.demo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Qualifier;
@Target({ElementType.TYPE, ElementType.METHOD,
ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Region {
Code value();
enum Code {
US, EU
}
}Add @Region(Region.Code.US) alongside @Component("pig") on the original Pig class. Leave Cow unannotated, then replace the farm's constructor with:
// Fragment inside Farm.java; other members stay unchanged.
public Farm(@Region(Region.Code.US) Animal animal) {
this.animal = animal;
}The US qualifier now selects Pig through annotation metadata rather than its bean name. The enum gives you compiler-checked region values instead of arbitrary strings, but it doesn't prove that exactly one matching bean is registered. Custom qualifiers can also have multiple attributes, all of which must match the injection point's requirements.
Can Spring Inject by Name Without @Qualifier?
Yes, in applicable autowiring situations. If type matching leaves several candidates and no other selection indicator determines the result, Spring can match the injection-point name against a candidate's bean name or alias. That makes name matching a fallback, not a guarantee that a variable's name always controls injection.
Starting from the original example with neither bean primary, this constructor can select pig:
// Fragment inside Farm.java; requires retained parameter names.
public Farm(Animal pig) {
this.animal = pig;
}Since Spring Framework 6.1, Java constructor and method parameter-name fallback requires compilation with -parameters. That Java compiler flag retains parameter names for reflection. Field-name fallback doesn't depend on this parameter metadata.
Bean-name matching is case-sensitive: pig and Pig are different names. Use lowercase Java variable names, and prefer an explicit qualifier when the choice carries business meaning. Otherwise, a seemingly harmless parameter rename can change the dependency selection or make it ambiguous.
Common Mistakes
- Putting the qualifier on the wrong injection point. With an explicit constructor, annotate its parameter. A qualifier on the backing field alone doesn't qualify that constructor argument.
- Treating candidate metadata as a global preference. A class-level qualifier describes the bean; it doesn't force every consumer to select it.
- Using a qualifier as bean registration. Keep
@Component, a@Beanmethod, or another registration mechanism;@Qualifieralone doesn't register the object. - Adding another implementation without checking wiring. Keep a context-level regression test for important dependency choices, especially when several beans deliberately share a qualifier.
FAQ
When should I use @Qualifier instead of @Primary?
Use @Qualifier when a particular consumer needs a specific bean or domain role. Use @Primary when one eligible bean should be the default for single-valued dependencies.
Does @Qualifier override @Primary in Spring?
An injection-point @Qualifier can select a non-primary bean by excluding the primary bean from the eligible candidates. If several candidates still match the qualifier, a unique primary among them can still determine the result.
Can I use @Qualifier with constructor injection?
Yes. Put @Qualifier on the constructor parameter that needs disambiguation. If the component has only one constructor, that constructor doesn't need @Autowired.
Can Spring choose a bean by the injection-point name?
Yes, name matching can resolve an otherwise ambiguous dependency when applicable selection rules allow it. Matching is case-sensitive, and Java constructor or method parameter-name fallback requires -parameters with Spring Framework 6.1 and later.
What happens if a qualifier matches more than one bean?
For a single-valued dependency, Spring still needs one selected candidate; a unique primary among the matches can resolve the choice. If ambiguity remains, injection fails with NoUniqueBeanDefinitionException. For a qualified collection, multiple matching beans are intentional.
Further Reading
- Spring's qualifier reference — semantic filtering, custom annotations, collections, and name fallback.
- Using @Autowired — constructor injection and required dependencies.
- Selecting defaults with @Primary — preference among competing candidates.
- @Qualifier API documentation — supported annotation targets and custom qualifier composition.
- Qualifier candidate resolver API — metadata, bean-name, and alias matching details.
- Component scanning and naming — registration and generated component names.
- Spring Boot application startup — CommandLineRunner and application lifecycle behavior.
- Running Spring Boot with Maven — the spring-boot:run goal.
