Spring Boot provides powerful dependency injection features, allowing developers to manage beans effectively. Two key annotations, @Primary and @Qualifier, help resolve conflicts when multiple beans of the same type exist. In this article, we’ll explore their usage with a full example.
Understanding @Primary and @Qualifier
@Primary: Marks a bean as the default choice when multiple beans of the same type are available.@Qualifier: Specifies the exact bean to inject, overriding the default behavior of@Primary.
Example Scenario
Let’s consider a scenario where we have multiple implementations of a PaymentService interface: CreditCardPaymentService and PayPalPaymentService.
Step 1: Define the Interface
public interface PaymentService {
void processPayment(double amount);
}
Step 2: Implement Multiple Beans
import org.springframework.stereotype.Service;
@Service
public class CreditCardPaymentService implements PaymentService {
@Override
public void processPayment(double amount) {
System.out.println("Processing credit card payment of $" + amount);
}
}
import org.springframework.stereotype.Service;
import org.springframework.context.annotation.Primary;
@Primary // This makes CreditCardPaymentService the default bean
@Service
public class PayPalPaymentService implements PaymentService {
@Override
public void processPayment(double amount) {
System.out.println("Processing PayPal payment of $" + amount);
}
}
Step 3: Inject the Default Bean Using @Primary
import org.springframework.stereotype.Component;
@Component
public class PaymentProcessor {
private final PaymentService paymentService;
public PaymentProcessor(PaymentService paymentService) {
this.paymentService = paymentService;
}
public void makePayment(double amount) {
paymentService.processPayment(amount);
}
}
Since PayPalPaymentService is marked with @Primary, it will be injected by default.
Step 4: Override @Primary Using @Qualifier
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Qualifier;
@Component
public class AlternativePaymentProcessor {
private final PaymentService paymentService;
public AlternativePaymentProcessor(@Qualifier("creditCardPaymentService") PaymentService paymentService) {
this.paymentService = paymentService;
}
public void makePayment(double amount) {
paymentService.processPayment(amount);
}
}
Here, @Qualifier("creditCardPaymentService") ensures CreditCardPaymentService is injected instead of the default PayPalPaymentService.
Step 5: Run the Application
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class PaymentApplication implements CommandLineRunner {
private final PaymentProcessor paymentProcessor;
private final AlternativePaymentProcessor alternativePaymentProcessor;
public PaymentApplication(PaymentProcessor paymentProcessor, AlternativePaymentProcessor alternativePaymentProcessor) {
this.paymentProcessor = paymentProcessor;
this.alternativePaymentProcessor = alternativePaymentProcessor;
}
public static void main(String[] args) {
SpringApplication.run(PaymentApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
paymentProcessor.makePayment(100.0); // Uses PayPalPaymentService
alternativePaymentProcessor.makePayment(200.0); // Uses CreditCardPaymentService
}
}
Output
Processing PayPal payment of $100.0
Processing credit card payment of $200.0
Conclusion
By using @Primary, we set a default bean, and @Qualifier allows us to specify a different implementation when needed. This approach gives flexibility in managing multiple bean implementations within a Spring Boot application.