Как избежать циклических зависимостей в Spring beans со свойствами списка

#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 должен иметь список всех своих слушателей, какой хороший шаблон для использования, который сохранит независимость моих модулей во время сборки / тестирования?

Я могу придумать несколько шаблонов:

  1. Использование разных версий eventListener в разных контекстах
  2. Использование нового компонента для облегчения регистрации
  3. Внутри каждого 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)