Как стать автором
Обновить

Комментарии 19

Допустим у вас есть несколько реализаций какой то вещи. В лист заинжектятся все имеющиеся в проекте реализации и вы выберете нужную и вызовете ее.
Речь про фильтрованный список. Не могу придумать, зачем это нужно в реальном мире.
Список всех реализаций некоего интерфейса я придумал куда применить можно было бы.

Расширю вопрос выше: зачем вообще в конфигурации прописывать конкретные списки инжектируемых реализаций?


Как по мне, так инъекция коллекции для того и нужна чтобы получить сразу все реализации.
Если вам нужны не все, то и реализовывать это нужно явно не через прибивание гвоздями того что нужно в код "конфигурации".


Если отказаться от хард-кода, то сразу появляется несколько вариантов, включающие как использование аннотаций @Qualifier, так и инъекция через конструктор с модификацией полной коллекции до требуемого варианта — в зависимости от конкретных потребностей.


P.S.
В документации к @Autowired явно указано как он будет работать для вариантов с инъекцией коллекций:


In case of a {@link java.util.Collection} or {@link java.util.Map}
dependency type, the container will autowire all beans matching the
declared value type. In case of a Map, the keys must be declared as
type String and will be resolved to the corresponding bean names.
1) Зачем? А зачем вообще курсы нужны, зачем какие-то пазлеры проводят, зачем у коллег спрашивают, если есть документация?)

2) Qualifier не позволит создать несколько разных бинов с одним именем, каким образом вы предлагаете их заинжектить?

3) По поводу решения с inject'ом того, что предлагает Spring: я правильно понимаю, что вы предлагаете фильтровать каждый раз полученный List? Тогда логика отбора нужных бинов уйдет в сервис, который должен их лишь использовать, что тоже выглядит странно.
Хоть вопрос задан и не мне, позволю себе ответить.

1) Вопрос «зачем» звучал про конкретную реализацию, а не паззлеры в общем. В Вашем примере в системе будут жить два Терминатора и два Рембо: один в виде самостоятельного Bean и ещё один как элемент коллекции. Какие проблемы могут возникнуть когда программист случайно подумает что их в системе только 1 штука, одному Гэндальфу известно.

2) Сам Spring, а не Qualifier не позволит создать несколько разных Bean с одним именем.

3) Совсем не обязательно, например, можно передавать в конструктор класса, требующего список action-героев, два параметра:
  1. Map<String, Hero> – коллекция порождённых системой Bean'ов
  2. Некий Bean, содержащий логику принадлежности к action-героям: (String, Hero) -&gt boolean. Или, если мудрить, (String, Hero) -&gt Set<EnumHeroType>

Естественно, процесс отображения всех hero-Bean'ов в список action-героев произойдёт один раз, во время инициализации системы.
1) Зачем? А зачем вообще курсы нужны, зачем какие-то пазлеры проводят, зачем у коллег спрашивают, если есть документация?)

Документация для того и нужна чтобы её читать и использовать инструмент по назначению, а не получать странное поведение, с которым потом приходится бороться странными методами.


А пазлеры не относятся к текущей статье.


2) Qualifier не позволит создать несколько разных бинов с одним именем, каким образом вы предлагаете их заинжектить?

@Qualifier не создаёт бины — он подсказывает какие именно бины нужно инжектить.
Требования уникальности имён бинов, как уже сказали выше, зашито в самом фреймворке.


3) По поводу решения с inject'ом того, что предлагает Spring: я правильно понимаю, что вы предлагаете фильтровать каждый раз полученный List? Тогда логика отбора нужных бинов уйдет в сервис, который должен их лишь использовать, что тоже выглядит странно.

Если сервису нужен какой-то срез коллекции всех бинов определённого типа, то так и так получается что именно сервис определяет логику этого среза.
В данной статье в качестве решения этой задачи (организации среза коллекции) предлагают зашить эту логику в конфигурацию в результате:


  • логика прибита гвоздями к коду и никак не настраивается
  • этот срез выдаёт одинаковый результат и на продовом коде и на тестовом — чтобы добавить тестовый бин в этот срез нужно перерабатывать саму конфигурацию, вместо того чтобы просто добавить бин в тестовом скопе

Есть решения более подходящие, на мой взгляд, например:


  • Инжектить промежуточный бин, который в себя инжектит всю коллекцию, а возвращает необходимый срез
  • Инжектить всю коллекцию плюс бин обеспечивающий фильтрацию, передавать одно во второе и получить необходимый срез

Наверняка есть и ещё более корректные и функциональные варианты, но вариант описанный в статье выглядит явно велосипедным.


По моему мнению, статья решает неправильную проблему неправильными средствами.


И да, я повторюсь, все нюансы инжекта бинов в списки и мапы описаны в документации — достаточно её прочитать чтобы понять как оно работает, вместо того что раскрывать всем глаза на особенности реализации.

Если вы это используете в рамках обучения как пример что такое Qualifier я вам рекомендую придумать другой пример.


Я не представляю где такой паттерн может понадобиться, и лишь запутает учеников, которые ведь постараются его использовать поломав всю архитектуру. Вам нужен или список всех бинов одного типа (i.e., представлящих один интерфейс) ИЛИ нужно несколько заведомо известных конкретных бинов. Но список из конкретных бинов это какая-то странная смесь, и скорее всего означает что проблема в архитектуре, не надо к ней подталкивать.


К слову, я начал использовать Spring еще с версии 1.0, сделал на нем думаю не менее тысячи малых и не очень сервисов с тех пор, но ни разу не сталкивался с такой задачей где это было бы применимо. Qualifier отдельно, списки отдельно.

Это не пример того, что такое Qualifier, а скорее уже нюанс. В любом случае, спасибо за обратную связь!
НЛО прилетело и опубликовало эту надпись здесь
Тогда, если вам понадобится в одном списке герои боевиков, а в другом герои фэнтези, вы не разрулите эту ситуацию.

В этом случае вы скорее всего сделаете два интерфейса, ActionHero и FantasyHero (можно без методов) и пометите нужные классы бинов как наследники. В @Autowire списке указываете нужный тип @Autowired List<ActionHero> actionsHeroes.

Разве установка аннотации Primary для бина в @Configuration не даст желаемого результата?
А не проще было обычную фабрику сделать?

а чем метод, возвращающий список героев — не фабрика?

сайд-эффекты, неочевидное поведение, завязывание на реализацию внешнего фреймворка
К сожалению, часто неудачные примеры еще больше запутывают людей. Лучше бы привели пример из «реальной жизни» (часть рабочего кода)
Если надо получить сразу все реализации, то лучше использовать конструкцию вида
@Autowired(required = false)
private List<Hero> actionHeroes = ImmutableList.of(); //List.of() - from Java 9

Тогда инициализация контекста не свалится если в контексте не будет не одного герояю
Зарегистрируйтесь на Хабре, чтобы оставить комментарий