Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
@Primary в Spring Framework
@Primary — это аннотация в Spring Framework, которая указывает, какой bean следует использовать, когда для инъекции есть несколько кандидатов одного типа. Это решение для разрешения конфликтов при множественных реализациях одного интерфейса.
Определение и назначение
@Primary — это аннотация уровня класса или метода, которая отмечает bean как "предпочтительный" для автоматической инъекции:
public interface PaymentService {
void pay(double amount);
}
@Service
public class CreditCardPaymentService implements PaymentService {
public void pay(double amount) {
System.out.println("Платёж кредитной картой: " + amount);
}
}
@Service
@Primary // Эта реализация будет использована по умолчанию
public class PayPalPaymentService implements PaymentService {
public void pay(double amount) {
System.out.println("Платёж через PayPal: " + amount);
}
}
Проблема без @Primary
Когда есть несколько bean-ов одного типа, Spring не знает, какой выбрать:
@Service
public class OrderService {
private PaymentService paymentService; // Какой выбрать?
@Autowired
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
Возникает исключение: NoUniqueBeanDefinitionException: No qualifying bean of type 'PaymentService' is defined: expected single matching bean but found 2
Решение проблемы с @Primary
Аннотация @Primary указывает, какой bean выбрать по умолчанию:
public interface PaymentService {
void pay(double amount);
}
@Service
public class CreditCardPaymentService implements PaymentService {
public void pay(double amount) {
System.out.println("Кредитная карта: " + amount);
}
}
@Service
@Primary // Будет выбран этот bean
public class PayPalPaymentService implements PaymentService {
public void pay(double amount) {
System.out.println("PayPal: " + amount);
}
}
@Service
public class OrderService {
private final PaymentService paymentService;
@Autowired
public OrderService(PaymentService paymentService) {
// будет внедрён PayPalPaymentService
this.paymentService = paymentService;
}
}
@Primary с @Bean конфигурацией
Аннотация работает и с методами @Bean:
@Configuration
public class PaymentConfig {
@Bean
public PaymentService creditCardPayment() {
return new CreditCardPaymentService();
}
@Bean
@Primary // Этот bean будет выбран
public PaymentService paypalPayment() {
return new PayPalPaymentService();
}
}
@Primary vs @Qualifier
Оба механизма решают проблему множественных реализаций, но по-разному:
@Primary — глобальное предпочтение
@Service
@Primary
public class PayPalPaymentService implements PaymentService {
// Будет использоваться всегда, если не указано иное
}
@Service
public class OrderService {
@Autowired
private PaymentService paymentService; // Получит PayPalPaymentService
}
@Qualifier — специфичная инъекция
@Service
@Qualifier("creditCard")
public class CreditCardPaymentService implements PaymentService {}
@Service
@Qualifier("paypal")
public class PayPalPaymentService implements PaymentService {}
@Service
public class OrderService {
@Autowired
@Qualifier("creditCard") // Явно указываем какой bean нужен
private PaymentService paymentService;
}
Сравнение таблица
| Аспект | @Primary | @Qualifier |
|---|---|---|
| Область | Глобальная | Локальная (для конкретной инъекции) |
| Явность | Неявная | Явная |
| Использование | Когда есть очевидный "основной" bean | Когда нужна специфичная инъекция |
| Сложность | Проще (одна аннотация) | Сложнее (требует квалификатора везде) |
| Переопределение | Можно переопределить @Qualifier | Имеет приоритет над @Primary |
Практические примеры
Пример 1: Database конфигурация
@Configuration
public class DataSourceConfig {
@Bean
public DataSource mainDataSource() {
// Основная БД
return createDataSource("jdbc:mysql://localhost/main");
}
@Bean
@Primary
public DataSource readReplicaDataSource() {
// Replica — предпочтительная для чтения
return createDataSource("jdbc:mysql://localhost/replica");
}
}
@Service
public class UserService {
@Autowired
private DataSource dataSource; // Получит readReplicaDataSource
}
Пример 2: Логирование
public interface Logger {
void log(String message);
}
@Component
public class ConsoleLogger implements Logger {
public void log(String message) {
System.out.println(message);
}
}
@Component
@Primary // Используется по умолчанию
public class FileLogger implements Logger {
public void log(String message) {
// Запись в файл
}
}
@Service
public class AppService {
@Autowired
private Logger logger; // Получит FileLogger
}
Пример 3: Комбинирование @Primary и @Qualifier
@Service
@Qualifier("fast")
public class FastPaymentService implements PaymentService {}
@Service
@Primary
@Qualifier("secure")
public class SecurePaymentService implements PaymentService {}
@Service
public class OrderService {
@Autowired
private PaymentService defaultPayment; // Получит SecurePaymentService
@Autowired
@Qualifier("fast")
private PaymentService fastPayment; // Получит FastPaymentService
}
Когда использовать @Primary
- Когда есть очевидный "основной" bean — например, основная БД из нескольких replica
- Когда большинство инъекций используют один bean — избегаете повторения @Qualifier везде
- Для упрощения кода — одна аннотация вместо квалификаторов везде
Когда использовать @Qualifier
- Когда каждое использование нуждается в специфичной реализации
- Когда нет очевидного "основного" bean
- Для явности и читаемости — ясно видно, какой bean выбирается
Итог
@Primary — это удобный механизм для выбора bean по умолчанию, когда есть несколько реализаций одного интерфейса. Он полезен при наличии "основного" bean, но может быть переопределён через @Qualifier, если нужна специфичная инъекция.