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

Как внедрить все реализации в интерфейсе

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

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

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

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

# Как внедрить все реализации в интерфейсе

Задача и контекст

Внедрение всех реализаций интерфейса (injection of all implementations) - это техника, позволяющая получить все компоненты, которые реализуют определённый интерфейс, в один список или карту. Это полезно для создания расширяемых архитектур.

1. Spring Framework - List injection

Базовый пример

import org.springframework.stereotype.Component;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;

// Интерфейс стратегии обработки платежей
public interface PaymentProcessor {
    boolean supports(String type);
    void process(double amount);
}

// Реализация 1
@Component
public class CreditCardPaymentProcessor implements PaymentProcessor {
    @Override
    public boolean supports(String type) {
        return "credit_card".equals(type);
    }
    
    @Override
    public void process(double amount) {
        System.out.println("Processing credit card payment: " + amount);
    }
}

// Реализация 2
@Component
public class PayPalPaymentProcessor implements PaymentProcessor {
    @Override
    public boolean supports(String type) {
        return "paypal".equals(type);
    }
    
    @Override
    public void process(double amount) {
        System.out.println("Processing PayPal payment: " + amount);
    }
}

// Реализация 3
@Component
public class CryptocurrencyPaymentProcessor implements PaymentProcessor {
    @Override
    public boolean supports(String type) {
        return "crypto".equals(type);
    }
    
    @Override
    public void process(double amount) {
        System.out.println("Processing crypto payment: " + amount);
    }
}

// Сервис, который внедряет все реализации
@Service
public class PaymentService {
    private final List<PaymentProcessor> paymentProcessors;
    
    // Spring автоматически собирает все реализации PaymentProcessor в List
    @Autowired
    public PaymentService(List<PaymentProcessor> paymentProcessors) {
        this.paymentProcessors = paymentProcessors;
    }
    
    public void processPayment(String type, double amount) {
        for (PaymentProcessor processor : paymentProcessors) {
            if (processor.supports(type)) {
                processor.process(amount);
                return;
            }
        }
        throw new IllegalArgumentException("Unsupported payment type: " + type);
    }
}

2. Внедрение в карту (Map)

Использование @Qualifier для организации

// Интерфейс
public interface NotificationService {
    void send(String message);
}

// Реализации с квалификаторами
@Component("emailNotification")
public class EmailNotificationService implements NotificationService {
    @Override
    public void send(String message) {
        System.out.println("Sending email: " + message);
    }
}

@Component("smsNotification")
public class SmsNotificationService implements NotificationService {
    @Override
    public void send(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

@Component("pushNotification")
public class PushNotificationService implements NotificationService {
    @Override
    public void send(String message) {
        System.out.println("Sending push: " + message);
    }
}

// Сервис, собирающий все в карту
@Service
public class NotificationDispatcher {
    private final Map<String, NotificationService> notificationServices;
    
    @Autowired
    public NotificationDispatcher(Map<String, NotificationService> services) {
        // Карта автоматически собирается из всех @Component
        this.notificationServices = services;
    }
    
    public void notify(String channel, String message) {
        NotificationService service = notificationServices.get(channel);
        if (service != null) {
            service.send(message);
        } else {
            throw new IllegalArgumentException("Unknown notification channel: " + channel);
        }
    }
    
    public void notifyAll(String message) {
        notificationServices.values().forEach(s -> s.send(message));
    }
}

3. Пользовательские конфигурации

С использованием @Bean

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;

// Интерфейс стратегии валидации
public interface Validator {
    String getName();
    boolean validate(Object input);
}

// Реализации
public class EmailValidator implements Validator {
    @Override
    public String getName() {
        return "email";
    }
    
    @Override
    public boolean validate(Object input) {
        return input instanceof String && ((String)input).contains("@");
    }
}

public class PhoneValidator implements Validator {
    @Override
    public String getName() {
        return "phone";
    }
    
    @Override
    public boolean validate(Object input) {
        return input instanceof String && ((String)input).matches("\\d{10}");
    }
}

// Конфигурация
@Configuration
public class ValidatorConfiguration {
    @Bean
    public EmailValidator emailValidator() {
        return new EmailValidator();
    }
    
    @Bean
    public PhoneValidator phoneValidator() {
        return new PhoneValidator();
    }
    
    // Собираем все валидаторы в один бин
    @Bean
    public List<Validator> validators(
            EmailValidator emailValidator,
            PhoneValidator phoneValidator) {
        return List.of(emailValidator, phoneValidator);
    }
}

// Использование
@Service
public class ValidationService {
    private final List<Validator> validators;
    private final Map<String, Validator> validatorMap;
    
    @Autowired
    public ValidationService(List<Validator> validators) {
        this.validators = validators;
        this.validatorMap = validators.stream()
            .collect(java.util.stream.Collectors.toMap(
                Validator::getName,
                v -> v
            ));
    }
    
    public boolean validate(String type, Object input) {
        return validatorMap.containsKey(type) && 
               validatorMap.get(type).validate(input);
    }
}

4. Advanced: ObjectProvider для гибкого доступа

import org.springframework.beans.factory.ObjectProvider;

public interface DataConverter {
    String getFormat();
    String convert(Object data);
}

@Component("jsonConverter")
public class JsonConverter implements DataConverter {
    @Override
    public String getFormat() {
        return "json";
    }
    
    @Override
    public String convert(Object data) {
        return "{data: " + data + "}";
    }
}

@Component("xmlConverter")
public class XmlConverter implements DataConverter {
    @Override
    public String getFormat() {
        return "xml";
    }
    
    @Override
    public String convert(Object data) {
        return "<data>" + data + "</data>";
    }
}

@Service
public class ConversionService {
    private final ObjectProvider<DataConverter> converterProvider;
    
    @Autowired
    public ConversionService(ObjectProvider<DataConverter> converterProvider) {
        // ObjectProvider позволяет ленивую загрузку и доступ к метаданным
        this.converterProvider = converterProvider;
    }
    
    public String convert(String format, Object data) {
        // getIfAvailable() - безопасное получение одного
        DataConverter converter = converterProvider.stream()
            .filter(c -> c.getFormat().equals(format))
            .findFirst()
            .orElseThrow(() -> new IllegalArgumentException("Unknown format: " + format));
        return converter.convert(data);
    }
    
    public void listAllConverters() {
        converterProvider.stream()
            .map(DataConverter::getFormat)
            .forEach(System.out::println);
    }
}

5. Сложный пример: Registry pattern

public interface Command {
    String getName();
    void execute(String[] args);
}

@Component
public class HelpCommand implements Command {
    @Override
    public String getName() {
        return "help";
    }
    
    @Override
    public void execute(String[] args) {
        System.out.println("Help: Available commands...");
    }
}

@Component
public class ExitCommand implements Command {
    @Override
    public String getName() {
        return "exit";
    }
    
    @Override
    public void execute(String[] args) {
        System.exit(0);
    }
}

// Registry для команд
@Service
public class CommandRegistry {
    private final Map<String, Command> commands = new java.util.HashMap<>();
    
    @Autowired
    public CommandRegistry(List<Command> commandList) {
        // Регистрируем все команды
        for (Command cmd : commandList) {
            commands.put(cmd.getName(), cmd);
        }
    }
    
    public void execute(String name, String[] args) {
        Command command = commands.get(name);
        if (command != null) {
            command.execute(args);
        } else {
            System.out.println("Unknown command: " + name);
        }
    }
    
    public Set<String> getAvailableCommands() {
        return commands.keySet();
    }
}

6. Обработка порядка внедрения

import org.springframework.core.annotation.Order;

public interface Filter {
    void apply(String data);
}

@Component
@Order(1)  // Порядок выполнения
public class LoggingFilter implements Filter {
    @Override
    public void apply(String data) {
        System.out.println("Logging: " + data);
    }
}

@Component
@Order(2)
public class ValidationFilter implements Filter {
    @Override
    public void apply(String data) {
        System.out.println("Validating: " + data);
    }
}

@Component
@Order(3)
public class TransformationFilter implements Filter {
    @Override
    public void apply(String data) {
        System.out.println("Transforming: " + data);
    }
}

@Service
public class FilterChain {
    private final List<Filter> filters;
    
    @Autowired
    public FilterChain(List<Filter> filters) {
        // Фильтры будут в порядке @Order
        this.filters = filters;
    }
    
    public void process(String data) {
        for (Filter filter : filters) {
            filter.apply(data);
        }
    }
}

Ключевые пункты

  1. List injection - Spring собирает все реализации автоматически
  2. Map injection - использует имена бинов как ключи
  3. @Qualifier - уточняет конкретную реализацию
  4. @Order - управляет порядком в списке
  5. ObjectProvider - для гибкого доступа и ленивой загрузки
  6. Registry pattern - классический подход для нескольких реализаций

Это позволяет легко добавлять новые реализации без изменения существующего кода (Open/Closed принцип SOLID).

Как внедрить все реализации в интерфейсе | PrepBro