Understanding @Qualifier and @Primary in Spring Boot
When building Spring Boot applications, we often work with interfaces and multiple implementations of those interfaces. This is a great practice because it promotes loose coupling, testability, and flexibility. However, it can also lead to ambiguity for the Spring container when trying to decide which bean to inject. That’s where @Qualifier and @Primary come in.
Visualizing the Problem
Here’s a simple PlantUML diagram that illustrates the situation:
This diagram shows one interface (IPayment), two implementations (PaypalPaymentService and StripePaymentService), and a consumer class (PaymentService) that depends on the interface.
Without guidance, Spring Boot won’t know which implementation to inject.
The Problem: Ambiguity in Bean Injection
package com.ghcp.demo.service;
import com.ghcp.demo.qualifier.IPayment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PaymentService {
@Autowired
private IPayment payment;
public void processPayment(double amount) {
System.out.println("Payment processed: " + amount);
payment.pay(amount);
}
}When the application starts, Spring will fail with:
Field payment in com.ghcp.demo.service.PaymentService required a single bean, but 2 were found:
- paypalPaymentService: defined in file [D:\GenAIPairProgramming\target\classes\com\ghcp\demo\qualifier\PaypalPaymentService.class]
- stripePaymentService: defined in file [D:\GenAIPairProgramming\target\classes\com\ghcp\demo\qualifier\StripePaymentService.class]
This may be due to missing parameter name information
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumedThis happens because Spring finds two beans (paypalPaymentService and stripePaymentService) and doesn’t know which one to inject.
Solution 1: Use @Qualifier for Explicit Selection
The @Qualifier annotation lets us specify exactly which bean should be injected:
package com.ghcp.demo.service;
import com.ghcp.demo.qualifier.IPayment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class PaymentService {
@Autowired
@Qualifier("paypalPaymentService") // Change to "stripePaymentService" to use Stripe
private IPayment payment;
public void processPayment(double amount) {
System.out.println("Payment processed: " + amount);
payment.pay(amount);
}
}How does Spring know the bean name? By default, Spring uses the class name with the first letter in lowercase.
PaypalPaymentService → paypalPaymentService
StripePaymentService → stripePaymentService
Get Anil Goyal’s stories in your inbox
Join Medium for free to get updates from this writer.
We can also override this with:
@Service("myCustomBeanName")
public class PaypalPaymentService implements IPayment { ... }Solution 2: Use @Primary for a Default Bean
If we have one “most common” or “default” implementation, we can mark it with @Primary:
package com.ghcp.demo.qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
@Service
@Primary
public class StripePaymentService implements IPayment {
@Override
public void pay(double amount) {
System.out.println("Processing Stripe payment of $" + amount);
}
}Now, if we inject IPayment without using @Qualifier, Spring will choose the @Primary bean.
@Service
public class PaymentService {
@Autowired
private IPayment payment; // Will inject StripePaymentService
public void processPayment(double amount) {
payment.pay(amount);
}
}When to Use @Qualifier vs @Primary
✅ Use @Primary when:
- You have a default bean that should usually be injected.
- Most of the time, consumers of the interface need the same implementation.
- We want to reduce the need for repetitive @Qualifier usage.
✅ Use @Qualifier when:
- We need to inject a specific implementation that isn’t the default.
- Different consumers need different beans.
- We want fine-grained control over which bean is injected.
Summary
- If multiple beans of the same type exist, Spring needs help deciding which one to inject.
- @Qualifier → Explicitly specify which bean to inject.
- @Primary → Declare a default bean when ambiguity exists.
- Best Practice → Use @Primary for a default, and @Qualifier for specific cases.
This gives us both flexibility and clarity in managing multiple bean implementations in Spring Boot.










