#java #spring #dependency-injection #circular-dependency
#java #spring #внедрение зависимостей #циклическая зависимость
Вопрос:
Допустим, у меня есть проект Maven, использующий Spring Framework. Сам проект Maven состоит из трех модулей: common
, component_a
и component_b
. И давайте предположим, что это common
определяет компонент-прослушиватель событий со свойством listeners
, которое представляет собой список всего, что прослушивает события. Слушатели определены в модулях component_a
и component_b
.
Подводя итог:
applicationcontext-component_a.xml:
<beans>
<bean id="someListener" class="com.whatever.component_a.SomeListener"/>
<bean id="anotherListener" class="com.whatever.component_a.AnotherListener"/>
</beans>
applicationcontext-component_b.xml:
<beans>
<bean id="yetAnotherListener" class="com.whatever.component_b.YetAnotherListener"/>
</beans>
applicationcontext-common.xml:
<beans>
<bean id="eventListener" class="com.whatever.common.EventListenerImpl">
<property name="listeners">
<list>
<ref bean="someListener"/>
<ref bean="anotherListener"/>
<ref bean="yetAnotherListener"/>
</list>
</property>
</bean>
</beans>
(Я думаю, что я получил формат прямо здесь … но простите меня, если я этого не сделал)
Моя проблема в том, что теперь common
есть зависимость от обоих component_*
модулей, а не наоборот. Мой common
модуль больше не собирается должным образом, потому что мои интеграционные тесты не могут должным образом создать контейнер без этих других модулей.
Итак, учитывая тот факт, что my eventListener
должен иметь список всех своих слушателей, какой хороший шаблон для использования, который сохранит независимость моих модулей во время сборки / тестирования?
Я могу придумать несколько шаблонов:
- Использование разных версий
eventListener
в разных контекстах - Использование нового компонента для облегчения регистрации
- Внутри каждого
setEventListener()
вызова не забудьте вызватьeventListener.addListener(this);
Но мне любопытно, есть ли у Spring какой-либо встроенный способ сделать это лучше…
Комментарии:
1. Как вы
import
используете свои контексты?2. У вас действительно не должно быть ничего явного в вашем
common
базовом модуле; вместо этого используйте какой-то упаковочный модуль, и вы в любом случае можете ввести список компонентов, отсканированных компонентами.3. @SotiriosDelimanolis Мы вообще не используем импорт… У нас есть несколько контекстов для производства (через web.xml ) и наши наборы интеграционных тестов, и везде есть циклические зависимости, но каждая из них является полной, насколько это касается Spring… Конечный результат заключается в том, что проект отлично строится, но модули не могут быть созданы независимо.
4. @chrylis У меня действительно есть модуль, который упаковывает WAR (допустим, что это
component_b
). Но он использует конфигурацию контекста из XML-файлов как внутри себя, так иcommon
модуля. Это плохая практика?5. Не само по себе, но если вы делаете что-то вроде явной сборки списка слушателей вместо его сканирования, этого определенно не должно быть в вашем
common
модуле. Следуйте тем же правилам в Spring wiring, что и в Maven dependencies.
Ответ №1:
Что вы могли бы сделать вместо этого, так это автоматическое подключение списка слушателей в вашем основном модуле. Затем каждый другой модуль может просто предоставлять реализации этого интерфейса и надлежащее объявление componentscan / xml.
Mainmodule: (псевдокод)
@Service
ListenerService {
@Autowire
List<ListenerIntf> listeners;
public handleListeners() {
foreach listeners {
listener.onEvent();
}
}
}
Модуль 1:
@Component
@Order // if desired
Listener1 implements ListenerIntf {
public onEvent();
}
Комментарии:
1. И Spring автоматически заполнит
listeners
свойство каждой найденной им реализациейListenerIntf
?2. ДА. Все реализации интерфейса будут подключены. В дополнение к этому вы даже можете использовать аннотацию «Порядок» для автоматического упорядочивания этого списка. (в Spring 4)