@Qualifier ve @Primary
Giriş
Bir interface'in birden fazla implementasyonu olduğunda, Spring hangi bean'i enjekte edeceğini bilemez ve NoUniqueBeanDefinitionException fırlatır. @Primary ve @Qualifier bu problemi çözer. Bu derste birden fazla bean arasında seçim yapma stratejilerini öğreneceğiz.
Problem: Birden Fazla Implementasyon
public interface NotificationService {
void send(String message);
}
@Service
public class EmailNotificationService implements NotificationService {
@Override
public void send(String message) {
System.out.println("Email: " + message);
}
}
@Service
public class SmsNotificationService implements NotificationService {
@Override
public void send(String message) {
System.out.println("SMS: " + message);
}
}
@Service
public class OrderService {
// ❌ HATA! Spring hangisini enjekte edeceğini bilmiyor
public OrderService(NotificationService notificationService) { }
}
// NoUniqueBeanDefinitionException: expected single matching bean
// but found 2: emailNotificationService, smsNotificationService@Primary — Varsayılan Seçim
@Primary, bir interface'in birden fazla implementasyonu olduğunda varsayılan bean'i belirler:
@Service
@Primary // Varsayılan olarak bu enjekte edilir
public class EmailNotificationService implements NotificationService {
@Override
public void send(String message) {
System.out.println("Email: " + message);
}
}
@Service
public class SmsNotificationService implements NotificationService {
@Override
public void send(String message) {
System.out.println("SMS: " + message);
}
}
@Service
public class OrderService {
// ✅ @Primary sayesinde EmailNotificationService enjekte edilir
public OrderService(NotificationService notificationService) {
notificationService.send("Sipariş alındı"); // "Email: Sipariş alındı"
}
}@Qualifier — Spesifik Seçim
@Qualifier, bean adını belirterek hangi implementasyonun enjekte edileceğini açıkça söyler:
@Service
public class OrderService {
private final NotificationService emailService;
private final NotificationService smsService;
public OrderService(
@Qualifier("emailNotificationService") NotificationService emailService,
@Qualifier("smsNotificationService") NotificationService smsService) {
this.emailService = emailService;
this.smsService = smsService;
}
public void placeOrder(Order order) {
emailService.send("Sipariş onayı: " + order.getId());
smsService.send("Siparişiniz alındı: " + order.getId());
}
}Özel Bean Adı Verme
@Service("emailSender") // Özel bean adı
public class EmailNotificationService implements NotificationService { }
@Service("smsSender")
public class SmsNotificationService implements NotificationService { }
// Kullanım
public OrderService(
@Qualifier("emailSender") NotificationService email,
@Qualifier("smsSender") NotificationService sms) { }Custom Qualifier Annotation
Tekrar eden @Qualifier string'leri yerine kendi annotation'ınızı oluşturabilirsiniz:
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface EmailQualifier { }
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface SmsQualifier { }
@Service
@EmailQualifier
public class EmailNotificationService implements NotificationService { }
@Service
@SmsQualifier
public class SmsNotificationService implements NotificationService { }
// Kullanım — type-safe, refactoring-friendly
public OrderService(
@EmailQualifier NotificationService email,
@SmsQualifier NotificationService sms) { }Collection Injection
Bir interface'in tüm implementasyonlarını tek seferde enjekte edebilirsiniz:
@Service
public class NotificationManager {
private final List<NotificationService> services;
// Tüm NotificationService bean'leri liste olarak enjekte edilir
public NotificationManager(List<NotificationService> services) {
this.services = services;
}
public void notifyAll(String message) {
services.forEach(service -> service.send(message));
}
}
// Map injection — bean adı key olarak kullanılır
@Service
public class NotificationRouter {
private final Map<String, NotificationService> serviceMap;
public NotificationRouter(Map<String, NotificationService> serviceMap) {
this.serviceMap = serviceMap;
// {"emailNotificationService": EmailNotification, "smsNotificationService": SmsNotification}
}
public void send(String type, String message) {
NotificationService service = serviceMap.get(type + "NotificationService");
if (service != null) service.send(message);
}
}@Primary vs @Qualifier Karşılaştırma
| Özellik | @Primary | @Qualifier |
|---|---|---|
| Tanım yeri | Bean sınıfında | Injection noktasında |
| Seçim | Varsayılan (implicit) | Açık (explicit) |
| Birden fazla | Sadece 1 @Primary olabilir | Her injection noktası farklı olabilir |
| Kullanım | "Genelde bunu kullan" | "Tam olarak bunu kullan" |
@Qualifierher zaman@Primary'i override eder.
Best Practices
İpucu: Az sayıda implementasyon varsa
@Primaryyeterlidir. Çoklu seçim gerekiyorsa@Qualifierkullanın. En temiz yol ise custom qualifier annotation oluşturmaktır.
Uyarı: Bean adlarında string literal kullanmak (
@Qualifier("emailService")) kırılgandır. Sınıf adı değişirse çalışmayı durdurur. Custom qualifier annotation daha güvenlidir.
Özet
Birden fazla implementasyon olduğunda @Primary varsayılan bean'i, @Qualifier spesifik bean'i seçer. Collection injection ile tüm implementasyonları alabilirsiniz. Custom qualifier annotation'lar ile type-safe seçim yapabilirsiniz.
AI Asistan
Sorularını yanıtlamaya hazır