06 - Anotações @Primary e @Qualifier para Spring Bean
Por padrão, o Spring injeta dependências por tipo. Quando existe apenas um Bean de um determinado tipo, o Spring sabe exatamente qual Bean deve ser injetado.
Qual é o problema?
@Service
public class EmailNotificationService implements NotificationService {
}
@Service
public class UserService {
private final NotificationService notificationService;
public UserService(NotificationService notificationService) {
this.notificationService = notificationService;
}
}
# Since there is only one implementation of NotificationService, Spring injects automatically.
# The problem happens when there are multiple Beans of the same type.
@Service
public class EmailNotificationService implements NotificationService {
}
@Service
public class SmsNotificationService implements NotificationService {
}
# Now Spring doesn't know which implementation to inject.
@Service
public class UserService {
private final NotificationService notificationService;
public UserService(NotificationService notificationService) {
this.notificationService = notificationService;
}
}
A inicialização da aplicação falha com um erro semelhante a:
No qualifying bean of type 'NotificationService' available:
expected single matching bean but found 2
Solução usando anotações?
O Spring oferece duas anotações para resolver essa ambiguidade:
- @Primary
- @Qualifier
Ambas indicam ao Spring qual Bean deve ser injetado, mas elas resolvem o problema de maneiras diferentes.
Uso de @Primary e @Qualifier?
@Primary
marca um Bean como a escolha padrão sempre que múltiplos Beans do mesmo tipo existirem.
@Service
@Primary
public class EmailNotificationService implements NotificationService {
}
@Service
public class SmsNotificationService implements NotificationService {
}
Agora esta injeção funciona, o Spring injeta automaticamente EmailNotificationService
@Service
public class UserService {
private final NotificationService notificationService;
public UserService(NotificationService notificationService) {
this.notificationService = notificationService;
}
}
@Qualifier
Permite que você escolha explicitamente qual Bean injetar.
@Service("emailService")
public class EmailNotificationService implements NotificationService {
}
@Service("smsService")
public class SmsNotificationService implements NotificationService {
}
# Spring injects SmsNotificationService, even if another Bean
is marked as @Primary
@Service
public class UserService {
private final NotificationService notificationService;
public UserService(
@Qualifier("smsService")
NotificationService notificationService) {
this.notificationService = notificationService;
}
}
@Primary vs @Qualifier?
Quando usar @Primary e quando usar @Qualifier?
Use @Primary quando:
- Uma implementação é usada na maioria das vezes
- Você deseja um Bean padrão para a aplicação
- A maioria dos pontos de injeção deve receber a mesma implementação
NotificationService
├── EmailNotificationService (@Primary)
└── SmsNotificationService
Use @Qualifier quando:
- Serviços diferentes exigem implementações diferentes
- Você precisa de controle explícito sobre qual Bean é injetado
- Múltiplas implementações são igualmente importantes e não há um padrão óbvio
@Service
public class MarketingService {
public MarketingService(
@Qualifier("emailService")
NotificationService notificationService) {
}
}
@Service
public class EmergencyService {
public EmergencyService(
@Qualifier("smsService")
NotificationService notificationService) {
}
}
Ordem de Prioridade
Quando o Spring resolve dependências, ele segue esta ordem:
- @Qualifier (prioridade mais alta)
- @Primary
- Nome do Bean (em alguns cenários de resolução)
- Se a ambiguidade ainda existir, o Spring lança uma exceção
# Although EmailNotificationService is marked with @Primary, Spring injects SmsNotificationService because @Qualifier always takes precedence.
@Service
@Primary
public class EmailNotificationService implements NotificationService {
}
@Service("smsService")
public class SmsNotificationService implements NotificationService {
}
public UserService(
@Qualifier("smsService")
NotificationService notificationService) {
}