Как внедрить коллекцию бинов в 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.