Я задал общий вопрос Spring: Auto-cast Spring Beans, и несколько человек ответили, что он вызывает Spring.ApplicationContext.getBean()
следует избегать как можно больше. Это почему?
Как еще мне получить доступ к bean-компонентам, которые я настроил для создания Spring?
Я использую Spring в не-веб-приложении и планировал доступ к общему ApplicationContext
объекту, как описано LiorH .
поправка
Я принимаю ответ ниже, но вот альтернативный взгляд Мартина Фаулера, который обсуждает преимущества внедрения зависимостей по сравнению с использованием сервисного локатора (что по сути то же самое, что вызов завернутого ApplicationContext.getBean()
).
В частности, Фаулер заявляет: « С помощью локатора сервиса класс приложения запрашивает его [сервис] явно посредством сообщения локатору. При внедрении нет явного запроса, сервис появляется в классе приложения - отсюда и инверсия управления. Инверсия контроля - это общая черта фреймворков, но это то, что имеет свою цену. Это трудно понять и приводит к проблемам при попытке отладки. Поэтому в целом я предпочитаю избегать этого [Инверсия контроля ] если только мне это не нужно. Это не значит, что это плохо, просто я думаю, что нужно оправдываться над более простой альтернативой ».
new MyOtherClass()
объекта? Я знаю о @Autowired, но я когда-либо использовал его только на полях, и он разбивается наnew MyOtherClass()
...applicationContext.getBean
это не внедрение зависимостей: это прямой доступ к фреймворку, использующий его как локатор службы .Provider<Foo>
вместо aFoo
и вызываяprovider.get()
каждый раз, когда вам нужно новый экземпляр. Нет ссылки на сам контейнер, и вы можете легко создатьProvider
для тестирования.Причины, по которым сервисный локатор предпочтительнее инверсионного контроля (IoC):
Сервис Локатор намного, намного проще для других людей следовать в вашем коде. IoC - «волшебство», но программисты по техническому обслуживанию должны понимать ваши замысловатые конфигурации Spring и все множество мест, чтобы выяснить, как вы подключили свои объекты.
IoC ужасен для отладки проблем конфигурации. В некоторых классах приложений приложение не будет запускаться при неправильной настройке, и у вас может не быть возможности прокрутить то, что происходит с отладчиком.
IoC в основном основан на XML (аннотации улучшают ситуацию, но XML по-прежнему существует). Это означает, что разработчики не могут работать с вашей программой, если они не знают всех магических тегов, определенных Spring. Это недостаточно хорошо, чтобы знать Java больше. Это мешает программистам с меньшим опытом (то есть, на самом деле плохой дизайн - использовать более сложное решение, когда более простое решение, такое как Service Locator, будет соответствовать тем же требованиям). Кроме того, поддержка диагностики проблем XML намного слабее, чем поддержка проблем Java.
Внедрение зависимостей больше подходит для больших программ. Большую часть времени дополнительная сложность не стоит.
Часто Spring используется в случае, если вы «захотите изменить реализацию позже». Есть и другие способы достижения этого без сложности Spring IoC.
Для веб-приложений (Java EE WARs) контекст Spring эффективно связывается во время компиляции (если вы не хотите, чтобы операторы разбирались с контекстом в разнесенной войне). Вы можете сделать так, чтобы Spring использовал файлы свойств, но файлы сервлетов должны быть в заранее определенном месте, что означает, что вы не можете развернуть несколько сервлетов одновременно в одном окне. Вы можете использовать Spring с JNDI для изменения свойств во время запуска сервлета, но если вы используете JNDI для изменяемых администратором параметров, необходимость в самом Spring уменьшается (поскольку JNDI фактически является локатором служб).
С Spring вы можете потерять управление программой, если Spring отправляет ваши методы. Это удобно и работает для многих типов приложений, но не для всех. Вам может понадобиться контролировать поток программы, когда вам нужно создавать задачи (потоки и т. Д.) Во время инициализации или вам нужны изменяемые ресурсы, о которых Spring не знал, когда контент был связан с вашей WAR.
Spring очень хорош для управления транзакциями и имеет некоторые преимущества. Просто во многих ситуациях IoC может быть чрезмерно сложным и представлять необоснованную сложность для сопровождающих. Не используйте автоматически IoC, не подумав о способах его не использовать в первую очередь.
источник
Это правда, что включение класса в application-context.xml избавляет от необходимости использовать getBean. Однако даже это на самом деле не нужно. Если вы пишете отдельное приложение и не хотите включать свой класс драйвера в application-context.xml, вы можете использовать следующий код, чтобы Spring автоматически связывал зависимости драйвера:
Я должен был сделать это пару раз, когда у меня есть какой-то отдельный класс, который должен использовать какой-то аспект моего приложения (например, для тестирования), но я не хочу включать его в контекст приложения, потому что это не так. на самом деле часть приложения. Также обратите внимание, что это избавляет от необходимости искать бин, используя строковое имя, которое я всегда считал уродливым.
источник
@Autowired
аннотацией также.Одним из самых классных преимуществ использования чего-то вроде Spring является то, что вам не нужно соединять свои объекты вместе. Голова Зевса распадается, и ваши классы появляются полностью сформированными со всеми зависимостями, созданными и подключенными по мере необходимости. Это волшебно и фантастично.
Чем больше вы говорите
ClassINeed classINeed = (ClassINeed)ApplicationContext.getBean("classINeed");
, тем меньше магии вы получаете. Меньше кода почти всегда лучше. Если вашему классу действительно нужен компонент ClassINeed, почему вы просто не подключили его?Тем не менее, очевидно, что что-то должно создать первый объект. В вашем основном методе нет ничего плохого в том, чтобы получить один или два компонента с помощью getBean (), но вам следует избегать этого, потому что всякий раз, когда вы его используете, вы на самом деле не используете всю магию Spring.
источник
Мотивация - написать код, который явно не зависит от Spring. Таким образом, если вы решите переключать контейнеры, вам не нужно переписывать какой-либо код.
Думайте о контейнере как о чем-то невидимом для вашего кода, волшебно обеспечивающем его потребности без всяких вопросов.
Внедрение зависимостей является контрапунктом к шаблону «поиск сервисов». Если вы собираетесь искать зависимости по имени, вы можете также избавиться от контейнера DI и использовать что-то вроде JNDI.
источник
Использование
@Autowired
илиApplicationContext.getBean()
действительно то же самое. В обоих случаях вы получаете компонент, настроенный в вашем контексте, и в обоих случаях ваш код зависит от Spring. Единственное, чего вам следует избегать - это создать экземпляр ApplicationContext. Делай это только один раз! Другими словами, строка какследует использовать только один раз в вашем приложении.
источник
Идея заключается в том, что вы полагаетесь на внедрение зависимостей ( инверсия управления или IoC). То есть ваши компоненты настроены так, как им нужно. Эти зависимости вводятся (через конструктор или сеттеры) - тогда вы сами не получите.
ApplicationContext.getBean()
требует, чтобы вы назвали бин явно внутри вашего компонента. Вместо этого, используя IoC, ваша конфигурация может определить, какой компонент будет использоваться.Это позволяет вам легко перемонтировать ваше приложение с различными реализациями компонентов или настроить объекты для тестирования простым способом, предоставляя имитированные варианты (например, поддельный DAO, чтобы вы не попали в базу данных во время тестирования)
источник
Другие указали на общую проблему (и являются правильными ответами), но я просто предложу еще один комментарий: это не значит, что вы НИКОГДА не должны делать этого, а скорее делать это как можно меньше.
Обычно это означает, что это делается ровно один раз: во время начальной загрузки. И тогда это просто доступ к «корневому» компоненту, с помощью которого можно разрешить другие зависимости. Это может быть повторно используемый код, например базовый сервлет (при разработке веб-приложений).
источник
Одно из помещений Spring - избегать сцепления . Определите и используйте интерфейсы, DI, AOP и избегайте использования ApplicationContext.getBean () :-)
источник
Одна из причин - тестируемость. Скажем, у вас есть этот класс:
Как вы можете проверить этот боб? Например, вот так:
Легко, правда?
Хотя вы по-прежнему зависите от Spring (из-за аннотаций), вы можете удалить свою зависимость от Spring без изменения какого-либо кода (только определения аннотаций), и разработчику теста не нужно ничего знать о том, как работает Spring (возможно, он должен это делать в любом случае, но это позволяет просматривать и тестировать код отдельно от того, что делает весна).
Это все еще возможно сделать то же самое при использовании ApplicationContext. Однако тогда вам нужно издеваться над
ApplicationContext
огромным интерфейсом. Вам либо нужна фиктивная реализация, либо вы можете использовать фальшивый фреймворк, такой как Mockito:Это вполне возможно, но я думаю, что большинство людей согласятся, что первый вариант более элегантный и упрощает тестирование.
Единственный вариант, который действительно является проблемой, это:
Тестирование этого требует огромных усилий, иначе ваш компонент будет пытаться подключиться к stackoverflow в каждом тесте. И как только у вас произойдет сбой в сети (или администраторы в stackoverflow блокируют вас из-за чрезмерной скорости доступа), у вас будут случайно провальные тесты.
Таким образом, в качестве заключения я бы не сказал, что использование
ApplicationContext
напрямую автоматически неправильно и его следует избегать любой ценой. Однако, если есть лучшие варианты (и есть в большинстве случаев), используйте лучшие варианты.источник
Я обнаружил только две ситуации, когда требуется getBean ():
Другие упоминали об использовании getBean () в main () для извлечения «основного» bean-компонента для отдельной программы.
Другое использование getBean (), которое я использовал, - это ситуации, когда интерактивная конфигурация пользователя определяет структуру bean-компонента для конкретной ситуации. Так, например, часть загрузочной системы проходит по таблице базы данных, используя getBean () с определением компонента scope = 'prototype', а затем устанавливает дополнительные свойства. Предположительно, существует пользовательский интерфейс, который настраивает таблицу базы данных, что было бы более удобным, чем попытка (пере) переписать контекст приложения XML.
источник
Есть другое время, когда использование getBean имеет смысл. Если вы реконфигурируете систему, которая уже существует, где зависимости явно не вызываются в контекстных файлах Spring. Вы можете начать процесс, введя вызовы getBean, чтобы вам не приходилось подключать все сразу. Таким образом, вы можете постепенно наращивать конфигурацию пружины, со временем размещая каждый элемент на месте и правильно расставляя биты. Вызовы getBean в конечном итоге будут заменены, но, как только вы поймете структуру кода или его отсутствие, вы можете начать процесс подключения все большего числа компонентов и использовать все меньше и меньше вызовов getBean.
источник
однако, все еще есть случаи, когда вам нужен шаблон поиска сервисов. например, у меня есть bean-компонент контроллера, у этого контроллера могут быть некоторые служебные bean-компоненты по умолчанию, которые могут быть зависимы от конфигурации. в то время как также может быть много дополнительных или новых сервисов, этот контроллер может вызывать сейчас или позже, которые затем нуждаются в локаторе сервисов для получения сервисных бинов.
источник
Вы должны использовать: ConfigurableApplicationContext вместо ApplicationContext
источник