Какой синтаксис можно использовать в @Query?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое @Primary?
@Primary — это аннотация в Spring, которая указывает что это bean должен использоваться по умолчанию, когда есть несколько реализаций одного интерфейса и нет явного @Qualifier. Это решает конфликт автоматического связывания (autowiring) зависимостей.
Проблема: несколько реализаций
Конфликт при автоматическом внедрении
// Интерфейс
public interface PaymentService {
void pay(double amount);
}
// Несколько реализаций
@Service
public class CreditCardPayment implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("Paying with credit card: " + amount);
}
}
@Service
public class PayPalPayment implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("Paying with PayPal: " + amount);
}
}
// Попытка внедрить
@RestController
public class OrderController {
@Autowired
private PaymentService paymentService; // ОШИБКА!
// Spring не знает какую реализацию использовать
// NoUniqueBeanDefinitionException
}
Решение 1: @Primary
@Primary указывает какую реализацию использовать по умолчанию
// Интерфейс
public interface PaymentService {
void pay(double amount);
}
// Primary реализация
@Service
@Primary // используется по умолчанию
public class CreditCardPayment implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("Paying with credit card: " + amount);
}
}
// Альтернативная реализация
@Service
public class PayPalPayment implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("Paying with PayPal: " + amount);
}
}
// Теперь работает!
@RestController
public class OrderController {
@Autowired
private PaymentService paymentService; // получит CreditCardPayment
@PostMapping("/order")
public void createOrder() {
paymentService.pay(100); // использует CreditCardPayment
}
}
Решение 2: @Qualifier (альтернатива)
@Qualifier явно указывает какую реализацию использовать
// Интерфейс
public interface PaymentService {
void pay(double amount);
}
// Реализация 1
@Service("creditCard")
public class CreditCardPayment implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("Paying with credit card: " + amount);
}
}
// Реализация 2
@Service("paypal")
public class PayPalPayment implements PaymentService {
@Override
public void pay(double amount) {
System.out.println("Paying with PayPal: " + amount);
}
}
// Использование
@RestController
public class OrderController {
@Autowired
@Qualifier("creditCard") // явно выбираем
private PaymentService creditCardService;
@Autowired
@Qualifier("paypal") // явно выбираем другую
private PaymentService paypalService;
@PostMapping("/order")
public void createOrder(String paymentMethod) {
if (paymentMethod.equals("cc")) {
creditCardService.pay(100);
} else if (paymentMethod.equals("paypal")) {
paypalService.pay(100);
}
}
}
@Primary с @Bean
@Primary можно использовать с @Bean в @Configuration
// Интерфейс
public interface DataSource {
Connection getConnection();
}
// Конфигурация
@Configuration
public class DataSourceConfig {
@Bean
@Primary // по умолчанию
public DataSource primaryDataSource() {
return new PostgresDataSource("jdbc:postgresql://localhost/prod");
}
@Bean("backupDataSource")
public DataSource backupDataSource() {
return new PostgresDataSource("jdbc:postgresql://localhost/backup");
}
}
// Использование
@Service
public class UserService {
@Autowired
private DataSource dataSource; // получит primaryDataSource
@Autowired
@Qualifier("backupDataSource")
private DataSource backupDb; // явно выбираем резервную
}
Сравнение: @Primary vs @Qualifier
| Аспект | @Primary | @Qualifier |
|---|---|---|
| Цель | Устанавливает default | Явно выбирает конкретную |
| Когда использовать | Один bean нужен чаще | Разные beans для разных целей |
| Читаемость | Код использования чище | Явно видно что используется |
| Конфликты | Решает автоматически | Требует явного указания |
| Ошибки | NoUniqueBeanDefinitionException | NoSuchBeanDefinitionException |
Практический пример: кэш
// Интерфейс
public interface CacheService {
void put(String key, Object value);
Object get(String key);
}
// Реализации
@Service
@Primary // используется для простых случаев
public class InMemoryCache implements CacheService {
private Map<String, Object> cache = new HashMap<>();
@Override
public void put(String key, Object value) {
cache.put(key, value);
}
@Override
public Object get(String key) {
return cache.get(key);
}
}
@Service("redisCache")
public class RedisCache implements CacheService {
@Autowired
private RedisTemplate redisTemplate;
@Override
public void put(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
@Override
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
}
// Использование
@Service
public class UserService {
@Autowired
private CacheService cache; // получит InMemoryCache
public User findUser(String id) {
User user = (User) cache.get("user:" + id);
if (user == null) {
user = loadFromDb(id);
cache.put("user:" + id, user);
}
return user;
}
}
@Service
public class OrderService {
@Autowired
@Qualifier("redisCache") // нужен масштабируемый кэш
private CacheService cache;
public Order findOrder(String id) {
Order order = (Order) cache.get("order:" + id);
if (order == null) {
order = loadFromDb(id);
cache.put("order:" + id, order);
}
return order;
}
}
Наследование и @Primary
@Primary работает с наследованием
// Базовый класс
public class Vehicle {
public void start() {
System.out.println("Vehicle starting");
}
}
// Наследники
@Service
@Primary
public class Car extends Vehicle {
@Override
public void start() {
System.out.println("Car starting");
}
}
@Service
public class Truck extends Vehicle {
@Override
public void start() {
System.out.println("Truck starting");
}
}
// Использование
@Service
public class TransportService {
@Autowired
private Vehicle vehicle; // получит Car (@Primary)
public void startJourney() {
vehicle.start(); // Car starting
}
}
Сложный пример: несколько интерфейсов
// Два интерфейса
public interface Database {
void query(String sql);
}
public interface Cache {
void set(String key, String value);
}
// Реализации
@Service
@Primary
public class DatabaseImpl implements Database {
@Override
public void query(String sql) { }
}
@Service
public class CacheImpl implements Cache {
@Override
public void set(String key, String value) { }
}
// @Primary работает отдельно для каждого интерфейса
@Service
public class UserRepository {
@Autowired
private Database db; // получит DatabaseImpl
@Autowired
private Cache cache; // получит CacheImpl
}
// Множественные реализации одного интерфейса
public interface Logger {
void log(String message);
}
@Service
@Primary
public class ConsoleLogger implements Logger {
@Override
public void log(String message) { }
}
@Service("fileLogger")
public class FileLogger implements Logger {
@Override
public void log(String message) { }
}
@Service
public class Application {
@Autowired
private Logger logger; // получит ConsoleLogger
@Autowired
@Qualifier("fileLogger")
private Logger fileLogger; // явно выбираем FileLogger
}
Лучшие практики
1. Используй @Primary для "common case"
@Service
@Primary // большинство случаев
public class LocalCache implements CacheService { }
@Service
public class DistributedCache implements CacheService { }
2. Используй @Qualifier для специфичных случаев
@Service
public class DataProcessor {
@Autowired // default
private CacheService cache;
@Autowired
@Qualifier("distributedCache") // специфично для этого поля
private CacheService clusterCache;
}
3. Комбинируй для ясности
@Configuration
public class DatabaseConfig {
@Bean
@Primary // primary для DataSource
public DataSource dataSource() { }
@Bean
public DataSource readOnlyDataSource() { }
@Bean
public DataSource analyticsDataSource() { }
}
4. НЕ переусложняй
// ❌ Плохо: множество @Primary
@Primary
public class ServiceA implements MyService { }
@Primary
public class ServiceB implements MyService { }
// ✅ Хорошо: одна @Primary + @Qualifier для остальных
@Primary
public class ServiceA implements MyService { }
@Service
public class ServiceB implements MyService { }
Когда использовать @Primary
Используй @Primary когда:
- Есть одна "очевидная" реализация по умолчанию
- 90% случаев используют одну реализацию
- Это упрощает код в большинстве мест
Используй @Qualifier когда:
- Разные компоненты нужны в разных местах
- Выбор зависит от условия/конфигурации
- Нужна явность для понимания
Вывод
@Primary — это способ указать Spring какой bean использовать по умолчанию при наличии нескольких реализаций одного интерфейса. Это упрощает autowiring когда одна реализация используется чаще.
Ключевые моменты:
- Только ОДНА реализация может быть @Primary
- @Primary работает автоматически при autowiring
- @Qualifier имеет приоритет над @Primary
- Используй для "common case", @Qualifier для специфичных случаев
Это стандартный паттерн в Spring для управления несколькими реализациями интерфейсов.