ContextLoaderListener czy nie?

122

Standardowa aplikacja internetowa Spring (stworzona przez Roo lub szablon „Spring MVC Project”) tworzy plik web.xml z użyciem ContextLoaderListeneri DispatcherServlet. Dlaczego nie tylko używają DispatcherServleti sprawiają, że ładują całą konfigurację?

Rozumiem, że ContextLoaderListener powinien być używany do ładowania rzeczy, które nie są istotne dla sieci, a DispatcherServlet służy do ładowania odpowiednich rzeczy internetowych (kontrolerów, ...). W rezultacie powstają dwa konteksty: kontekst rodzica i kontekst dziecka.

Tło:

Robiłem to w ten standardowy sposób przez kilka lat.

<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>

Często powodowało to problemy z dwoma kontekstami i zależnościami między nimi. W przeszłości zawsze byłem w stanie znaleźć rozwiązanie i mam silne przeczucie, że dzięki temu struktura / architektura oprogramowania jest zawsze lepsza. Ale teraz mam problem z wydarzeniami z obu kontekstów .

- Jednak to zmusza mnie do ponownego przemyślenia tych dwóch wzorców kontekstowych i zadaję sobie pytanie: dlaczego miałbym się w to pakować, dlaczego nie załadować wszystkich plików konfiguracyjnych sprężyny jednym DispatcherServleti ContextLoaderListenercałkowicie usunąć . (Nadal będę mieć różne pliki konfiguracyjne, ale tylko jeden kontekst).

Czy jest jakiś powód, aby nie usuwać ContextLoaderListener?

Ralph
źródło
„To często powodowało problemy z dwoma kontekstami i zależnościami między nimi”. To świetny przykład tego, jak uważam, że ramy wstrzykiwania zależności po prostu utrudniają nam życie niż zastrzyk zależności zrób to sam.
Andy
1
@Andy - Chociaż trochę sympatyzuję z tym punktem widzenia, nie mogę nie zauważyć, że przypadki użycia, dla których potrzebujesz obu kontekstów (współdzielenie obiektów między filtrami bezpieczeństwa i serwletami, automatyczne zarządzanie transakcjami, aby były zamykane po wyświetleniu na które przekierowujesz, zakończył rendering) są dość trudne do osiągnięcia bez pomocy frameworka. Dzieje się tak głównie dlatego, że API serwletów najwyraźniej nigdy nie zostało zaprojektowane do pracy z wstrzykiwaniem zależności i aktywnie działa przeciwko tobie, jeśli spróbujesz to zrobić samodzielnie.
Periata Breatta
@PeriataBreatta Widzę! Czy myślisz, że gdyby został zaprojektowany inaczej, to byłyby lepsze alternatywy dla Spring MVC? Chociaż ludzie i tak mogli zaprojektować kompletne alternatywy dla Servlet API ...
Andy
@PeriataBreatta Warto zauważyć, że w świecie JS, gdzie używam Express do routingu żądań HTTP od około roku, rzadko widzę wzmiankę o „wstrzykiwaniu zależności” i nic nie przypomina frameworka Springa.
Andy

Odpowiedzi:

86

W twoim przypadku nie, nie ma powodu, aby zachować ContextLoaderListeneri applicationContext.xml. Jeśli twoja aplikacja działa dobrze tylko z kontekstem serwletu, to się przy tym trzyma, jest prostsze.

Tak, ogólnie zalecanym wzorcem jest trzymanie rzeczy spoza sieci w kontekście poziomu aplikacji internetowej, ale to nic innego jak słaba konwencja.

Jedyne istotne powody, dla których warto używać kontekstu na poziomie aplikacji internetowej, to:

  • Jeśli masz wiele, DispatcherServletktóre muszą udostępniać usługi
  • Jeśli masz serwlety starsze / inne niż Spring, które wymagają dostępu do usług przewodowych Spring
  • Jeśli masz filtrów serwletów że hak w kontekście webapp poziomu (np Wiosna Security DelegatingFilterProxy, OpenEntityManagerInViewFilteritp)

Żadne z nich nie dotyczy Ciebie, więc dodatkowa złożoność jest nieuzasadniona.

Po prostu zachowaj ostrożność podczas dodawania zadań w tle do kontekstu serwletu, takich jak zaplanowane zadania, połączenia JMS, itp. Jeśli zapomnisz dodać coś <load-on-startup>do swojego web.xml, zadania te nie zostaną uruchomione do pierwszego dostępu do serwletu.

skaffman
źródło
2
A co z odbiornikami, wydaje się, że potrzebują kontekstu utworzonego przez odbiornik modułu ładującego kontekst (IllegalStateException, nie znaleziono WebApplicationContext, wyzwalanego przez MultipartFilter, CharacterEncodingFilter, HiddenHttpMethodFilter, Spring Security DelegatingFilterProxy i OpenEntityManagerInViewFilter). Czy dobrym pomysłem jest zrobienie tego w drugą stronę (załaduj wszystko przez ContextLoaderListener i pozostaw DispatcherServlet bez konfiguracji)?
Ralph
@Ralph: Dobry chwyt, dodałem ten przypadek użycia do listy. Jeśli chodzi o wyjście DispatcherServletbez konfiguracji - gdybyś to zrobił, nie miałbyś interfejsu internetowego. Wszystkie rzeczy z MVC muszą tam trafić.
skaffman
2
@skaffman Dlaczego powinienem używać dwóch kontekstów, gdy używam zabezpieczenia sprężynowego z DelegatingFilterProxy? W moim przypadku fasolki bezpieczeństwa wiosennego i domyślny kontekst sprężyny mają kilka wspólnych. Dlatego też powinni mieć ten sam kontekst. A może wiosenne fasolki bezpieczeństwa powinny znajdować się poza domyślnym kontekstem sprężyny?
Matthias M
10

Możesz również skonfigurować kontekst aplikacji na odwrót. Np. Aby OpenEntityManagerInViewFilter działał. Skonfiguruj ContextLoaderListener, a następnie skonfiguruj DispatcherServlet za pomocą:

<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>

Po prostu upewnij się, że wartość parametru contextConfigLocation jest pusta.

Gunnar Hillert
źródło
1
Ale jaka jest zaleta tej konfiguracji? A co masz na myśli mówiąc „na odwrót”?
Ralph
Rozwiązanie stworzone przez „skaffmana” skonfigurowało tylko kontekst aplikacji WWW (serwlet). Jednak przy takim podejściu napotkasz problemy wyszczególnione w samym rozwiązaniu: „Jedynymi istotnymi powodami używania kontekstu na poziomie aplikacji internetowej są:”… ”Jeśli masz filtry serwletów, które przechwytują się w kontekście na poziomie aplikacji sieci Web (np. Spring Security's DelegatingFilterProxy, OpenEntityManagerInViewFilter itp.) „Jeśli chcesz używać tylko jednego pliku XML kontekstu aplikacji, myślę, że moje rozwiązanie (określenie XML za pomocą ContextLoaderListener) byłoby lepsze.
Gunnar Hillert
czy możesz używać kontrolera sieci Web MVC w kontekście utworzonym przez nasłuchiwanie kontekstu?
Ralph
1
Tak. Po prostu skonfigurowałbyś kontrolery w pliku context.xml określonym przez nasłuchiwanie kontekstu. Sposób działania polega na tym, że DispatcherServlet po prostu dołączy do „kontekstu aplikacji nadrzędnej” (nasłuchiwania kontekstu). W przypadku pozostawienia pustej wartości „contextConfigLocation” plik context.xml określony przez nasłuchiwanie kontekstu będzie używany wyłącznie.
Gunnar Hillert,
1
Myślę, że w swoim kontekście przegapiłeś <mvc: annotation-Based />. Rozwiązanie @GunnarHillert działa dla mnie.
milbr
10

Chcę podzielić się tym, co zrobiłem w mojej aplikacji Spring-MVC:

  1. Na we-mvc-config.xmldodałam zaledwie klas adnotacją z @Controller:

    <context:component-scan base-package="com.shunra.vcat">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
  2. Na applicationContext.xmlplikach dodałem całą resztę:

    <context:component-scan base-package="com.shunra.vcat">
        <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
Modi
źródło
Tak, to przydatny wzór. Innym użytecznym wzorcem jest po prostu umieszczenie fasoli obsługujących bazę danych w kontekście aplikacji (prawdopodobnie będą one potrzebne dla OpenSessionInViewFilter lub podobnego) wraz z wszystkim, co jest potrzebne filtrom lub odbiorcom (np. Definicje niezbędne do korzystania z zabezpieczeń sprężynowych).
Periata Breatta