ContextLoaderListener или нет?

122

Стандартное веб-приложение Spring (созданное Roo или шаблоном «Spring MVC Project») создает файл web.xml с помощью ContextLoaderListenerи DispatcherServlet. Почему они не только используют DispatcherServletи заставляют загружать полную конфигурацию?

Я понимаю, что ContextLoaderListener следует использовать для загрузки материалов, не относящихся к сети, а DispatcherServlet используется для загрузки материалов, относящихся к сети (контроллеры, ...). И это приводит к двум контекстам: родительскому и дочернему.

Задний план:

Я делал это стандартным образом несколько лет.

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Handles Spring requests -->
<servlet>
    <servlet-name>roo</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/spring/webmvc-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

Это часто вызывало проблемы с двумя контекстами и зависимостями между ними. В прошлом мне всегда удавалось найти решение, и у меня есть сильное чувство, что это всегда улучшает структуру / архитектуру программного обеспечения. Но теперь я столкнулся с проблемой событий обоих контекстов .

- Однако это заставляет меня переосмыслить этот шаблон с двумя контекстами, и я спрашиваю себя: почему я должен ввязываться в эту проблему, почему бы не загрузить все файлы конфигурации Spring с одним DispatcherServletи ContextLoaderListenerполностью удалить файл . (У меня по-прежнему будут разные файлы конфигурации, но только один контекст.)

Есть ли причина не удалять ContextLoaderListener?

Ральф
источник
«Это часто вызывало проблемы с двумя контекстами и зависимостями между ними». Это отличный пример того, как, на мой взгляд, фреймворки для внедрения зависимостей просто усложняют нашу жизнь, чем самостоятельное внедрение зависимостей.
Энди
1
@Andy - Хотя я немного симпатизирую этой точке зрения, я не могу не заметить, что варианты использования, для которых вам нужны оба контекста (совместное использование объектов между фильтрами безопасности и сервлетами, автоматическое управление транзакциями, чтобы они закрывались после просмотра который вы перенаправляете на законченный рендеринг) довольно сложно добиться без помощи фреймворка. В основном это связано с тем, что API сервлетов явно никогда не предназначался для работы с внедрением зависимостей и активно работает против вас, если вы попытаетесь сделать это самостоятельно.
Periata Breatta,
@PeriataBreatta Понятно! Что ж, как вы думаете, если бы он был разработан по-другому, были бы лучшие альтернативы Spring MVC? Хотя люди все равно могли бы разработать полные альтернативы API сервлетов ...
Энди
@PeriataBreatta Интересно отметить, что в мире JS, где я использую Express для маршрутизации HTTP-запросов около года, я редко вижу упоминания о «внедрении зависимостей» и вообще ничего похожего на структуру Spring.
Энди

Ответы:

86

В вашем случае нет, незачем оставлять ContextLoaderListenerи applicationContext.xml. Если ваше приложение отлично работает только с контекстом сервлета, придерживаться этого будет проще.

Да, обычно поощряемый шаблон состоит в том, чтобы хранить не веб-материалы в контексте уровня веб-приложений, но это не более чем слабое соглашение.

Единственные веские причины использовать контекст уровня веб-приложений:

  • Если у вас несколько DispatcherServletкомпаний, которым нужно поделиться услугами
  • Если у вас есть устаревшие / не-Spring сервлеты, которым требуется доступ к службам Spring-wired
  • Если у вас есть сервлет фильтры, крючок в контексте WebApp уровня (например , Spring Security - х DelegatingFilterProxy, OpenEntityManagerInViewFilterи т.д.)

К вам ничего из этого не относится, поэтому дополнительная сложность неоправданна.

Просто будьте осторожны при добавлении фоновых задач в контекст сервлета, таких как запланированные задачи, соединения JMS и т. Д. Если вы забудете добавить <load-on-startup>в свой web.xml, эти задачи не будут запущены до первого обращения к сервлету.

skaffman
источник
2
Что касается слушателей, похоже, что им нужен Context, созданный слушателем Context Loader (IllegalStateException, WebApplicationContext не найден, запускается MultipartFilter, CharacterEncodingFilter, HiddenHttpMethodFilter, Spring Security DelegatingFilterProxy и OpenEntityManagerInViewFilter). Это хорошая идея сделать это наоборот (загрузить все с помощью ContextLoaderListener и оставить DispatcherServlet без конфигурации)?
Ralph
@ Ральф: Хороший улов, я добавил этот вариант использования в список. Что касается выхода DispatcherServletбез конфигурации - если бы вы это сделали, у вас не было бы веб-интерфейса. Все материалы MVC должны быть там.
skaffman
2
@skaffman Почему я должен использовать два контекста при использовании spring -security с DelegatingFilterProxy? В моем случае компоненты Spring-Security и контекст Spring по умолчанию используют некоторые компоненты. Таким образом, они также должны иметь общий контекст. Или компоненты безопасности Spring должны быть исключены из контекста Spring по умолчанию?
Matthias M
10

Вы можете настроить контекст приложения и наоборот. Например, чтобы заставить OpenEntityManagerInViewFilter работать. Настройте ContextLoaderListener, а затем настройте свой DispatcherServlet с помощью:

<servlet>
    <servlet-name>spring-mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value></param-value>
    </init-param>
</servlet>

Просто убедитесь, что значение параметра contextConfigLocation пустое.

Гуннар Хиллер
источник
1
Но в чем преимущество этой конфигурации? А что значит «наоборот»?
Ральф
Решение от «skaffman» настраивало только контекст веб-приложения (сервлет). Однако при таком подходе вы столкнетесь с проблемами, подробно описанными в самом решении: «Единственными вескими причинами для использования контекста уровня webapp являются:« ... »Если у вас есть фильтры сервлетов, которые подключаются к контексту уровня webbapp (например, DelegatingFilterProxy Spring Security, OpenEntityManagerInViewFilter и т. Д.) «Если вы хотите использовать только один XML-файл контекста приложения, я думаю, что мое решение (указание XML через ContextLoaderListener) будет предпочтительнее.
Gunnar Hillert
Можете ли вы использовать веб-контроллер MVC в контексте, созданном прослушивателем контекста?
Ральф
1
Да. Вы должны просто настроить свои контроллеры в файле context.xml, указанном прослушивателем контекста. Это работает так, что DispatcherServlet просто присоединяется к «контексту родительского приложения» (прослушиватель контекста). Если оставить значение contextConfigLocation пустым, файл context.xml, указанный прослушивателем контекста, будет использоваться исключительно.
Gunnar Hillert
1
Я думаю, вы пропустили <mvc: annotation-driven /> в своем контексте. Решение @GunnarHillert работает для меня.
milbr
10

Я хочу поделиться тем, что я сделал в своем приложении Spring-MVC:

  1. На we-mvc-config.xmlя добавил только классы аннотированные с @Controller:

    <context:component-scan base-package="com.shunra.vcat">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
  2. По applicationContext.xmlфайлам добавил все остальное:

    <context:component-scan base-package="com.shunra.vcat">
        <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
Моди
источник
Да, это полезный шаблон. Другой полезный шаблон - просто поместить компоненты обработки базы данных в контекст приложения (они, вероятно, понадобятся для OpenSessionInViewFilter или аналогичного) вместе со всем, что конкретно необходимо фильтрам или слушателям (например, определениям, необходимым для использования безопасности Spring).
Periata Breatta,