Как внедрить коллекцию бинов в Spring

Если точек расширения несколько, Spring может внедрить все бины совместимого типа в массив, List или Map<String, T>.

interface CommandHandler {
    boolean supports(Command command);
    void handle(Command command);
}
 
@Component
@Order(10)
final class CreateHandler implements CommandHandler {
    public boolean supports(Command command) {
        return command instanceof CreateCommand;
    }
 
    public void handle(Command command) {
        // обработка создания
    }
}
 
@Component
@Order(20)
final class DeleteHandler implements CommandHandler {
    public boolean supports(Command command) {
        return command instanceof DeleteCommand;
    }
 
    public void handle(Command command) {
        // обработка удаления
    }
}
 
@Service
final class CommandDispatcher {
    private final List<CommandHandler> handlers;
    private final Map<String, CommandHandler> handlersByBeanName;
 
    CommandDispatcher(
            List<CommandHandler> handlers,
            Map<String, CommandHandler> handlersByBeanName
    ) {
        this.handlers = List.copyOf(handlers);
        this.handlersByBeanName = Map.copyOf(handlersByBeanName);
    }
}

Для Map<String, CommandHandler> ключи — имена бинов, значения — подходящие экземпляры.

Порядок и фильтрация

  • @Order или Ordered задаёт порядок списка/массива;
  • @Priority также может участвовать в сортировке;
  • это порядок внедрения, а не гарантия порядка создания бинов;
  • @Qualifier на точке коллекции фильтрует кандидатов с этим qualifier;
  • generic-тип тоже участвует в выборе кандидатов.
CommandDispatcher(
        @Qualifier("external") List<CommandHandler> handlers
) {
    this.handlers = handlers;
}

Если набор действительно необязателен, удобно отложенно получить его:

CommandDispatcher(ObjectProvider<CommandHandler> provider) {
    this.handlers = provider.orderedStream().toList();
}

Не регистрировать один общий изменяемый ArrayList как замену множеству стратегий: тогда Spring уже не видит отдельные lifecycle и qualifiers.

Ошибки

  • полагаться на случайный порядок без @Order;
  • использовать имя bean как бизнес-идентификатор без явного контракта;
  • внедрять Set и ожидать сортировку как у List;
  • изменять полученную коллекцию и считать, что контейнер автоматически подхватит новые элементы.

На собеседовании

Что попадёт в List<Handler>? Все подходящие бины типа Handler с учётом qualifiers.

Какие ключи у Map<String, Handler>? Имена бинов.

Гарантирует ли @Order порядок инициализации? Нет, он упорядочивает кандидатов в таких точках.

Как получить необязательный набор? Например, через ObjectProvider<T>.orderedStream().

Связано: Spring Dependency Injection и Inversion of Control, Основные аннотации Spring.