Как собрать все реализации Bean в компоненте в Spring
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Сбор всех реализаций Bean в Spring
Часто нужно собрать все реализации определенного интерфейса или получить несколько бинов одного типа. Spring предоставляет для этого несколько механизмов.
Способ 1: List или Set через автовпрыск
Самый простой способ — автоматически собрать все реализации интерфейса в коллекцию.
Определение интерфейса и реализаций
public interface PaymentProcessor {
String getName();
void process(Payment payment);
}
@Component
public class StripePaymentProcessor implements PaymentProcessor {
@Override
public String getName() {
return "stripe";
}
@Override
public void process(Payment payment) {
System.out.println("Processing via Stripe");
}
}
@Component
public class PayPalPaymentProcessor implements PaymentProcessor {
@Override
public String getName() {
return "paypal";
}
@Override
public void process(Payment payment) {
System.out.println("Processing via PayPal");
}
}
@Component
public class ApplePayProcessor implements PaymentProcessor {
@Override
public String getName() {
return "apple-pay";
}
@Override
public void process(Payment payment) {
System.out.println("Processing via Apple Pay");
}
}
Сбор всех реализаций
@Service
public class PaymentService {
private final List<PaymentProcessor> processors;
// Spring автоматически инъектирует ВСЕ реализации PaymentProcessor
public PaymentService(List<PaymentProcessor> processors) {
this.processors = processors;
}
public void processPayment(String providerName, Payment payment) {
// Найти нужный процессор
PaymentProcessor processor = processors.stream()
.filter(p -> p.getName().equals(providerName))
.findFirst()
.orElseThrow(() -> new PaymentProviderNotFoundException(providerName));
processor.process(payment);
}
public List<String> getAvailablePaymentMethods() {
return processors.stream()
.map(PaymentProcessor::getName)
.collect(Collectors.toList());
}
}
Способ 2: Map<String, Interface>
Если нужна быстрая подстановка по ключу, используй Map.
Конфигурация
@Configuration
public class PaymentProcessorConfig {
@Bean
public Map<String, PaymentProcessor> paymentProcessors(
StripePaymentProcessor stripe,
PayPalPaymentProcessor paypal,
ApplePayProcessor applePay) {
Map<String, PaymentProcessor> processors = new HashMap<>();
processors.put("stripe", stripe);
processors.put("paypal", paypal);
processors.put("apple-pay", applePay);
return processors;
}
}
@Service
public class PaymentService {
private final Map<String, PaymentProcessor> processors;
public PaymentService(Map<String, PaymentProcessor> processors) {
this.processors = processors;
}
public void processPayment(String providerName, Payment payment) {
PaymentProcessor processor = processors.get(providerName);
if (processor == null) {
throw new PaymentProviderNotFoundException(providerName);
}
processor.process(payment);
}
}
Способ 3: ObjectProvider для ленивой загрузки
ObjectProvider используется, когда нужна ленивая загрузка или проверка доступности бинов.
@Service
public class PaymentService {
private final ObjectProvider<PaymentProcessor> processorProvider;
public PaymentService(ObjectProvider<PaymentProcessor> processorProvider) {
this.processorProvider = processorProvider;
}
public void processPayment(String providerName, Payment payment) {
// Получить ALL бины, которые доступны
List<PaymentProcessor> allProcessors = processorProvider.stream()
.filter(p -> p.getName().equals(providerName))
.collect(Collectors.toList());
if (allProcessors.isEmpty()) {
throw new PaymentProviderNotFoundException(providerName);
}
allProcessors.get(0).process(payment);
}
public Optional<PaymentProcessor> findProcessor(String name) {
return processorProvider.stream()
.filter(p -> p.getName().equals(name))
.findFirst();
}
}
Способ 4: ListableBeanFactory
Для получения бинов по типу из контекста.
@Service
public class PaymentService {
private final ListableBeanFactory beanFactory;
public PaymentService(ListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void processPayment(String providerName, Payment payment) {
// Получить ВСЕ бины типа PaymentProcessor
Map<String, PaymentProcessor> processors =
beanFactory.getBeansOfType(PaymentProcessor.class);
PaymentProcessor processor = processors.values().stream()
.filter(p -> p.getName().equals(providerName))
.findFirst()
.orElseThrow(() -> new PaymentProviderNotFoundException(providerName));
processor.process(payment);
}
}
Способ 5: @Qualifier для выбора конкретной реализации
Когда нужна конкретная реализация, а не все.
@Service
public class OrderService {
private final PaymentProcessor stripeProcessor;
private final PaymentProcessor paypalProcessor;
public OrderService(
@Qualifier("stripePaymentProcessor") PaymentProcessor stripe,
@Qualifier("paypalPaymentProcessor") PaymentProcessor paypal) {
this.stripeProcessor = stripe;
this.paypalProcessor = paypal;
}
public void processOrder(Order order) {
if (order.preferredPayment() == "stripe") {
stripeProcessor.process(order.getPayment());
} else {
paypalProcessor.process(order.getPayment());
}
}
}
Способ 6: Пользовательские Qualifier аннотации
Для более гибкого выбора бинов.
Определение Qualifier
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface PaymentMethod {
String value();
}
Использование Qualifier
@Component
@PaymentMethod("stripe")
public class StripePaymentProcessor implements PaymentProcessor {
// ...
}
@Component
@PaymentMethod("paypal")
public class PayPalPaymentProcessor implements PaymentProcessor {
// ...
}
@Service
public class PaymentService {
private final PaymentProcessor stripeProcessor;
public PaymentService(@PaymentMethod("stripe") PaymentProcessor processor) {
this.stripeProcessor = processor;
}
}
Способ 7: @Primary для выбора по умолчанию
Когда есть несколько реализаций, но одна должна быть основной.
@Component
@Primary // Эта реализация выбирается по умолчанию
public class StripePaymentProcessor implements PaymentProcessor {
@Override
public String getName() {
return "stripe";
}
@Override
public void process(Payment payment) {
System.out.println("Processing via Stripe (DEFAULT)");
}
}
@Component
public class PayPalPaymentProcessor implements PaymentProcessor {
@Override
public String getName() {
return "paypal";
}
@Override
public void process(Payment payment) {
System.out.println("Processing via PayPal");
}
}
@Service
public class PaymentService {
private final PaymentProcessor defaultProcessor; // Инъектируется StripePaymentProcessor
public PaymentService(PaymentProcessor defaultProcessor) {
this.defaultProcessor = defaultProcessor;
}
}
Способ 8: Условная регистрация с @ConditionalOnProperty
Регистрировать реализации только если они включены в конфигурации.
@Component
@ConditionalOnProperty(name = "payment.stripe.enabled", havingValue = "true")
public class StripePaymentProcessor implements PaymentProcessor {
// Загружается только если payment.stripe.enabled=true
}
@Component
@ConditionalOnProperty(name = "payment.paypal.enabled", havingValue = "true")
public class PayPalPaymentProcessor implements PaymentProcessor {
// Загружается только если payment.paypal.enabled=true
}
Практический пример: Payment Gateway
// Интерфейс
public interface PaymentGateway {
String getProviderName();
PaymentResult process(PaymentRequest request);
}
// Реализации
@Component
public class StripeGateway implements PaymentGateway {
@Override
public String getProviderName() {
return "stripe";
}
@Override
public PaymentResult process(PaymentRequest request) {
return new PaymentResult(true, "Payment processed via Stripe");
}
}
@Component
public class PayPalGateway implements PaymentGateway {
@Override
public String getProviderName() {
return "paypal";
}
@Override
public PaymentResult process(PaymentRequest request) {
return new PaymentResult(true, "Payment processed via PayPal");
}
}
// Сервис с использованием всех реализаций
@Service
public class PaymentProcessingService {
private final Map<String, PaymentGateway> gateways;
public PaymentProcessingService(List<PaymentGateway> gatewayList) {
// Преобразуем List в Map для быстрого доступа
this.gateways = gatewayList.stream()
.collect(Collectors.toMap(
PaymentGateway::getProviderName,
Function.identity()
));
}
public PaymentResult processPayment(PaymentRequest request, String providerName) {
PaymentGateway gateway = gateways.get(providerName);
if (gateway == null) {
throw new PaymentProviderNotFoundException(providerName);
}
return gateway.process(request);
}
public List<String> getAvailableProviders() {
return new ArrayList<>(gateways.keySet());
}
}
// REST Controller
@RestController
@RequestMapping("/api/payments")
public class PaymentController {
private final PaymentProcessingService service;
public PaymentController(PaymentProcessingService service) {
this.service = service;
}
@PostMapping("/process")
public ResponseEntity<PaymentResult> processPayment(
@RequestBody PaymentRequest request,
@RequestParam String provider) {
PaymentResult result = service.processPayment(request, provider);
return ResponseEntity.ok(result);
}
@GetMapping("/providers")
public ResponseEntity<List<String>> getProviders() {
return ResponseEntity.ok(service.getAvailableProviders());
}
}
Вывод
Методы сбора всех реализаций:
- List<Interface> — простейший способ
- Map<String, Interface> — для быстрого поиска
- ObjectProvider — для ленивой загрузки
- ListableBeanFactory — для программного доступа
- @Qualifier — для выбора конкретной реализации
- Пользовательские @Qualifier — для сложной логики
- @Primary — для выбора по умолчанию
- @ConditionalOnProperty — для условной регистрации
Выбор способа зависит от сценария: List для простоты, Map для быстрого доступа, ObjectProvider для гибкости.