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

Как собрать все реализации Bean в компоненте в Spring

2.0 Middle🔥 221 комментариев
#Spring Boot и Spring Data

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

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

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

Сбор всех реализаций 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());
    }
}

Вывод

Методы сбора всех реализаций:

  1. List<Interface> — простейший способ
  2. Map<String, Interface> — для быстрого поиска
  3. ObjectProvider — для ленивой загрузки
  4. ListableBeanFactory — для программного доступа
  5. @Qualifier — для выбора конкретной реализации
  6. Пользовательские @Qualifier — для сложной логики
  7. @Primary — для выбора по умолчанию
  8. @ConditionalOnProperty — для условной регистрации

Выбор способа зависит от сценария: List для простоты, Map для быстрого доступа, ObjectProvider для гибкости.

Как собрать все реализации Bean в компоненте в Spring | PrepBro