Dung (Donny) Nguyen

Senior Software Engineer

Qualifier

In the Spring Framework, @Qualifier is an annotation used in the context of Dependency Injection (DI) to resolve ambiguity when multiple beans of the same type are present in the Spring application context. When Spring encounters multiple beans of the same type and needs to autowire one of them, it doesn’t know which one to choose by default. The @Qualifier annotation helps specify exactly which bean should be injected.

When to Use @Qualifier

How @Qualifier Works

@Qualifier works alongside the @Autowired annotation (or other injection annotations like @Inject) to specify which bean should be injected. Here’s how we can use it:

  1. Define Multiple Beans: Suppose we have an interface Vehicle with two implementations: Car and Bike.

     public interface Vehicle {
         void move();
     }
    
     @Component
     @Qualifier("car")
     public class Car implements Vehicle {
         @Override
         public void move() {
             System.out.println("Car is moving");
         }
     }
    
     @Component
     @Qualifier("bike")
     public class Bike implements Vehicle {
         @Override
         public void move() {
             System.out.println("Bike is moving");
         }
     }
    

    Alternatively, we can use the @Component("car") and @Component("bike") annotations to assign specific bean names.

  2. Inject the Desired Bean: Use @Autowired along with @Qualifier to inject the specific bean we need.

     @Component
     public class VehicleService {
    
         private final Vehicle vehicle;
    
         @Autowired
         public VehicleService(@Qualifier("car") Vehicle vehicle) {
             this.vehicle = vehicle;
         }
    
         public void startJourney() {
             vehicle.move();
         }
     }
    

    In this example, VehicleService will be injected with the Car bean instead of Bike.

Alternative Usage: Field Injection

While constructor injection is recommended for better testability and immutability, @Qualifier can also be used with field injection:

@Component
public class VehicleService {

    @Autowired
    @Qualifier("bike")
    private Vehicle vehicle;

    public void startJourney() {
        vehicle.move();
    }
}

Combining with @Primary

Spring also provides the @Primary annotation, which can be used to designate a default bean when multiple candidates are present. If one bean is marked as @Primary, it will be chosen by default unless another bean is explicitly specified with @Qualifier.

@Component
@Primary
public class Car implements Vehicle {
    @Override
    public void move() {
        System.out.println("Car is moving");
    }
}

@Component
public class Bike implements Vehicle {
    @Override
    public void move() {
        System.out.println("Bike is moving");
    }
}

In this case, Car will be injected by default unless a different bean is specified using @Qualifier.

Benefits of Using @Qualifier

Best Practices

  1. Prefer Constructor Injection: It’s generally recommended to use constructor injection over field injection for better testability and immutability.

  2. Use Descriptive Qualifiers: When naming qualifiers, use clear and descriptive names that convey the purpose or type of the bean.

  3. Combine with Profiles: @Qualifier can be used alongside Spring Profiles to inject different beans based on the active profile.

  4. Limit Bean Ambiguity: While @Qualifier helps resolve ambiguities, strive to design our application in a way that minimizes the need for multiple beans of the same type unless necessary.

Example Scenario

Imagine we are building an application that sends notifications through different channels, such as email and SMS. We might have an interface NotificationService with two implementations: EmailNotificationService and SmsNotificationService.

public interface NotificationService {
    void sendNotification(String message);
}

@Component
@Qualifier("email")
public class EmailNotificationService implements NotificationService {
    @Override
    public void sendNotification(String message) {
        // Send email
    }
}

@Component
@Qualifier("sms")
public class SmsNotificationService implements NotificationService {
    @Override
    public void sendNotification(String message) {
        // Send SMS
    }
}

In our service that needs to send notifications, we can specify which NotificationService to inject:

@Service
public class NotificationManager {

    private final NotificationService notificationService;

    @Autowired
    public NotificationManager(@Qualifier("email") NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    public void notifyUser(String message) {
        notificationService.sendNotification(message);
    }
}

If we later decide to switch to SMS notifications, we can simply change the qualifier:

@Autowired
public NotificationManager(@Qualifier("sms") NotificationService notificationService) {
    this.notificationService = notificationService;
}

Conclusion

The @Qualifier annotation in Spring Framework is a powerful tool for managing dependencies when multiple beans of the same type exist. By explicitly specifying which bean to inject, @Qualifier helps maintain clear and maintainable code, especially in large and complex applications with numerous components and services.