← Назад к вопросам

Что такое @Primary?

2.0 Middle🔥 171 комментариев
#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

@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

  1. Когда есть очевидный "основной" bean — например, основная БД из нескольких replica
  2. Когда большинство инъекций используют один bean — избегаете повторения @Qualifier везде
  3. Для упрощения кода — одна аннотация вместо квалификаторов везде

Когда использовать @Qualifier

  1. Когда каждое использование нуждается в специфичной реализации
  2. Когда нет очевидного "основного" bean
  3. Для явности и читаемости — ясно видно, какой bean выбирается

Итог

@Primary — это удобный механизм для выбора bean по умолчанию, когда есть несколько реализаций одного интерфейса. Он полезен при наличии "основного" bean, но может быть переопределён через @Qualifier, если нужна специфичная инъекция.