← Назад к вопросам
Что такое Primary в Spring?
2.0 Middle🔥 211 комментариев
#Spring Boot и Spring Data#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое @Primary в Spring?
Определение
@Primary — это аннотация Spring, которая указывает, что бин должен быть предпочтительным при внедрении зависимостей (dependency injection), когда доступно несколько реализаций одного интерфейса. Она разрешает амбигуальность между несколькими кандидатами для инъекции.
Проблема, которую она решает
// Интерфейс
public interface PaymentService {
void pay(double amount);
}
// Две реализации
@Component
public class CreditCardPayment implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("Платёж по кредитной карте: " + amount);
}
}
@Component
public class PayPalPayment implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("Платёж через PayPal: " + amount);
}
}
// Пытаемся внедрить
@Service
public class OrderService {
@Autowired
private PaymentService paymentService; // ❌ Ошибка!
// Spring не знает, какую реализацию выбрать
}
Ошибка:
Exception encountered during context initialization:
No qualifying bean of type 'PaymentService' available:
expected single matching bean but found 2
Решение с @Primary
Пример 1: Явное указание предпочтительного бина
public interface PaymentService {
void pay(double amount);
}
// Основная реализация
@Component
@Primary
public class CreditCardPayment implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("Платёж по кредитной карте: " + amount);
}
}
// Альтернативная реализация
@Component
public class PayPalPayment implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("Платёж через PayPal: " + amount);
}
}
// Теперь это работает!
@Service
public class OrderService {
@Autowired
private PaymentService paymentService; // Используется CreditCardPayment
public void processOrder(Order order) {
paymentService.pay(order.getTotal());
}
}
Различные сценарии использования
Пример 2: Использование конкретного бина
public interface DataSource {
Connection getConnection();
}
@Component
@Primary
public class PostgresDataSource implements DataSource {
@Override
public Connection getConnection() {
// PostgreSQL соединение
return null;
}
}
@Component
public class MySQLDataSource implements DataSource {
@Override
public Connection getConnection() {
// MySQL соединение
return null;
}
}
@Service
public class UserRepository {
// По умолчанию PostgreSQL
@Autowired
private DataSource dataSource;
}
Пример 3: Явное указание не-primary бина
@Service
public class OrderService {
private PaymentService primaryPayment;
private PaymentService alternativePayment;
public OrderService(
@Autowired PaymentService paymentService, // @Primary
@Autowired(required = false) @Qualifier("payPalPayment") PaymentService altPayment
) {
this.primaryPayment = paymentService;
this.alternativePayment = altPayment;
}
}
@Primary vs @Qualifier
Разница:
public interface NotificationService {
void notify(String message);
}
@Component
@Primary
public class EmailNotification implements NotificationService {
@Override
public void notify(String message) {
System.out.println("Email: " + message);
}
}
@Component
public class SmsNotification implements NotificationService {
@Override
public void notify(String message) {
System.out.println("SMS: " + message);
}
}
@Service
public class UserService {
private NotificationService primaryNotif;
private NotificationService smsNotif;
// Способ 1: @Primary
@Autowired
private NotificationService notificationService; // Email
// Способ 2: @Qualifier
@Autowired
@Qualifier("smsNotification")
private NotificationService smsService; // SMS
// Способ 3: Конструктор
public UserService(
@Qualifier("emailNotification") NotificationService email,
@Qualifier("smsNotification") NotificationService sms
) {
this.primaryNotif = email;
this.smsNotif = sms;
}
}
Когда использовать:
@Primary:
- Когда есть одна явно предпочтительная реализация
- Для упрощения кода (не нужно писать @Qualifier везде)
- Для значения "по умолчанию"
@Qualifier:
- Когда нужно явно указать конкретный бин
- Когда внедряем несколько реализаций одновременно
- Когда хотим быть максимально явным
С конфигурацией бинов
Пример с @Configuration:
@Configuration
public class PaymentConfig {
@Bean
@Primary
public PaymentService creditCardPayment() {
return new CreditCardPayment();
}
@Bean
public PaymentService payPalPayment() {
return new PayPalPayment();
}
@Bean
@Primary
public PaymentService stripePayment() {
return new StripePayment();
}
}
// Использование
@Service
public class OrderService {
@Autowired
private PaymentService paymentService; // Используется StripePayment
}
Приоритет при множественном @Primary
@Configuration
public class Config {
@Bean
@Primary
public PaymentService payment1() {
return new CreditCardPayment();
}
@Bean
@Primary // ❌ ОШИБКА! Два @Primary для одного типа!
public PaymentService payment2() {
return new PayPalPayment();
}
}
// Exception:
// No qualifying bean of type 'PaymentService' available:
// expected single matching bean but found 2
Реальный пример: Environment-specific beans
@Component
@Profile("development")
@Primary
public class DevelopmentDatabase implements DatabaseService {
private String connectionString = "jdbc:h2:mem:testdb";
@Override
public Connection getConnection() {
// H2 in-memory база для разработки
return null;
}
}
@Component
@Profile("production")
@Primary
public class ProductionDatabase implements DatabaseService {
private String connectionString = "jdbc:postgresql://prod-server:5432/db";
@Override
public Connection getConnection() {
// PostgreSQL production база
return null;
}
}
@Service
public class UserRepository {
@Autowired
private DatabaseService database; // Используется в зависимости от профиля
}
С List и Map инъекциями
public interface Logger {
void log(String message);
}
@Component
@Primary
public class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println(message);
}
}
@Component
public class FileLogger implements Logger {
@Override
public void log(String message) {
// Запись в файл
}
}
@Service
public class LoggingService {
// Если нужен List, @Primary игнорируется
@Autowired
private List<Logger> loggers; // Список всех реализаций
// Если нужна Map, @Primary всё равно важен
@Autowired
private Map<String, Logger> loggerMap;
// А если просто одна переменная - используется @Primary
@Autowired
private Logger logger; // ConsoleLogger
}
Лучшие практики
// ✅ 1. Используй @Primary для очевидного выбора
@Component
@Primary
public class DefaultPaymentService implements PaymentService {
// Это основной способ платежа
}
// ✅ 2. Используй @Qualifier для уточнения
@Service
public class OrderService {
@Autowired
@Qualifier("backup-payment")
private PaymentService backupPayment;
}
// ✅ 3. Документируй выбор
@Component
@Primary // Используется для 95% платежей
public class CreditCardPayment implements PaymentService {
}
// ❌ 4. НЕ используй несколько @Primary для одного типа
// ❌ 5. НЕ смешивай @Primary и @Qualifier без необходимости
Debug и тестирование
@SpringBootTest
public class PaymentServiceTest {
@Autowired
private PaymentService paymentService;
@Test
public void testPrimaryBean() {
// paymentService будет CreditCardPayment
assertThat(paymentService).isInstanceOf(CreditCardPayment.class);
}
}
// Для тестирования с другой реализацией
@SpringBootTest
@Import(TestPaymentConfig.class)
public class AlternativePaymentTest {
@Autowired
private PaymentService paymentService;
@Test
public void testAlternativeBean() {
assertThat(paymentService).isInstanceOf(MockPaymentService.class);
}
}
@Configuration
class TestPaymentConfig {
@Bean
@Primary
public PaymentService testPayment() {
return new MockPaymentService();
}
}
Итог
@Primary — это простой и элегантный способ разрешить амбигуальность при множественных реализациях интерфейса. Она:
- Указывает, какой бин использовать по умолчанию
- Уменьшает необходимость в @Qualifier
- Делает код чище и понятнее
- Работает с @Component, @Bean и конфигурацией
Основное правило: используй @Primary для явно предпочтительной реализации и @Qualifier когда нужна конкретная альтернатива.