ContextLoaderListener o no?


122

Un'applicazione web standard di primavera (creata da Roo o modello "Spring MVC Project") crea un file web.xml con ContextLoaderListenere DispatcherServlet. Perché non usano solo DispatcherServlete per caricare la configurazione completa?

Capisco che ContextLoaderListener dovrebbe essere usato per caricare le cose che non sono rilevanti per il web e il DispatcherServlet viene usato per caricare le cose rilevanti per il web (controller, ...). E questo si traduce in due contesti: un contesto genitore e uno figlio.

Sfondo:

L'ho fatto in questo modo standard per diversi anni.

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

Ciò ha spesso causato problemi con i due contesti e le dipendenze tra di loro. In passato sono sempre stato in grado di trovare una soluzione, e ho la forte sensazione che questo renda sempre migliore la struttura / architettura del software. Ma ora sto affrontando un problema con gli eventi di entrambi i contesti .

- Tuttavia questo mi fa ripensare a questo schema a due contesti, e mi chiedo: perché dovrei mettermi in questo guaio, perché non caricare tutti i file di configurazione della molla con uno DispatcherServlete rimuovere ContextLoaderListenercompletamente il file. (Avrò ancora diversi file di configurazione, ma solo un contesto.)

C'è qualche motivo per non rimuovere il ContextLoaderListener?


"Questo spesso causava problemi con i due contesti e le dipendenze tra di loro". Questo è un ottimo esempio di come penso che i framework di inserimento delle dipendenze rendano le nostre vite più difficili dell'iniezione di dipendenze fai-da-te.
Andy

1
@ Andy - Anche se ho un po 'di simpatia per questo punto di vista, non posso fare a meno di notare che i casi d'uso per cui hai bisogno di entrambi i contesti (condivisione di oggetti tra filtri di sicurezza e servlet, gestione automatica delle transazioni in modo che vengano chiusi dopo la visualizzazione a cui si reindirizza ha terminato il rendering) sono abbastanza difficili da ottenere senza l'aiuto del framework. Ciò è principalmente dovuto al fatto che l'API servlet non è mai stata chiaramente progettata per funzionare con l'inserimento delle dipendenze e funziona attivamente contro di te se provi a farlo da solo.
Periata Breatta

@PeriataBreatta vedo! Ebbene, pensi che se fosse stato progettato diversamente ci sarebbero alternative migliori a Spring MVC? Anche se le persone avrebbero potuto progettare alternative complete all'API Servlet comunque ...
Andy

@PeriataBreatta È interessante notare che nel mondo JS, dove utilizzo Express per l'instradamento delle richieste HTTP da circa un anno, raramente vedo alcuna menzione di "iniezione di dipendenze" e nulla che assomigli affatto al framework Spring.
Andy,

Risposte:


86

Nel tuo caso, no, non c'è motivo di mantenere ContextLoaderListenere applicationContext.xml. Se la tua app funziona bene solo con il contesto del servlet, che rimane con quello, è più semplice.

Sì, lo schema generalmente consigliato è quello di mantenere le cose non web nel contesto a livello di webapp, ma non è altro che una convenzione debole.

Gli unici motivi validi per utilizzare il contesto a livello di webapp sono:

  • Se hai più DispatcherServletche necessitano di condividere servizi
  • Se si dispone di servlet legacy / non Spring che richiedono l'accesso ai servizi Spring cablati
  • Se si dispone di filtri servlet che gancio nel contesto di livello webapp (ad esempio di Primavera di sicurezza DelegatingFilterProxy, OpenEntityManagerInViewFilterecc)

Nessuno di questi si applica a te, quindi la complessità extra è ingiustificata.

Fai solo attenzione quando aggiungi attività in background al contesto del servlet, come attività pianificate, connessioni JMS, ecc. Se dimentichi di aggiungere <load-on-startup>al tuo web.xml, queste attività non verranno avviate fino al primo accesso del servlet.


2
E per quanto riguarda gli ascoltatori, sembra che abbiano bisogno del Context creato dal listener del Context Loader (IllegalStateException, No WebApplicationContext trovato, attivato da MultipartFilter, CharacterEncodingFilter, HiddenHttpMethodFilter, Spring Security DelegatingFilterProxy e OpenEntityManagerInViewFilter). È una buona idea fare il contrario (caricare ogni cosa da ContextLoaderListener e lasciare DispatcherServlet senza una configurazione)?
Ralph

@ Ralph: buona presa, ho aggiunto quel caso d'uso alla lista. Per quanto riguarda la partenza DispatcherServletsenza una configurazione, se lo facessi, non avresti un'interfaccia web. Tutta la roba MVC deve essere contenuta.
skaffman

2
@skaffman Perché dovrei utilizzare due contesti quando utilizzo spring-security con DelegatingFilterProxy? Nel mio caso i bean spring-security e il contesto spring predefinito condividono alcuni bean. Quindi anche loro dovrebbero condividere lo stesso contesto. O i bean di sicurezza di primavera dovrebbero essere tenuti fuori dal contesto di primavera predefinito?
Matthias M

10

Puoi configurare il contesto dell'applicazione anche al contrario. Ad esempio, per far funzionare OpenEntityManagerInViewFilter . Imposta ContextLoaderListener, quindi configura DispatcherServlet con:

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

Assicurati solo che il valore del parametro contextConfigLocation sia vuoto.


1
Ma qual è il vantaggio di questa configurazione? E cosa intendi con "il contrario"?
Ralph

La soluzione di "skaffman" ha configurato solo un contesto di applicazione web (servlet). Tuttavia, con questo approccio ti imbatti in problemi descritti in dettaglio nella soluzione stessa: "Gli unici motivi validi per utilizzare il contesto a livello di webapp sono:" ... "Se hai filtri servlet che si collegano al contesto a livello di webbapp (ad es. DelegatingFilterProxy di Spring Security, OpenEntityManagerInViewFilter, ecc.) "Se si desidera utilizzare solo 1 file XML del contesto dell'applicazione, penso che la mia soluzione (specificando l'XML tramite ContextLoaderListener) sarebbe preferibile.
Gunnar Hillert

sei in grado di utilizzare MVC Web Controller nel contesto creato dal Context Listener?
Ralph

1
Sì. Dovresti semplicemente impostare i tuoi controller nel file context.xml specificato dal Context Listener. Il modo in cui funziona è che DispatcherServlet si unirà semplicemente al "contesto dell'applicazione padre" (Context Listener). Quando si lascia vuoto il valore "contextConfigLocation", verrà utilizzato esclusivamente il file context.xml specificato dal Context Listener.
Gunnar Hillert

1
Penso che ti sei perso <mvc: annotation-driven /> nel tuo contesto. La soluzione @GunnarHillert funziona per me.
milbr

10

Voglio condividere quello che ho fatto sulla mia applicazione Spring-MVC:

  1. Sul we-mvc-config.xmlho aggiunto solo le classi annotate con @Controller:

    <context:component-scan base-package="com.shunra.vcat">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
  2. Sui applicationContext.xmlfile ho aggiunto tutto il resto:

    <context:component-scan base-package="com.shunra.vcat">
        <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>

Sì, questo è un modello utile. Un altro modello utile è semplicemente inserire i bean di gestione del database nel contesto dell'applicazione (è probabile che siano necessari per un OpenSessionInViewFilter o simili) insieme a qualsiasi cosa specificamente necessaria ai filtri o ai listener (ad esempio, le definizioni necessarie per utilizzare la protezione spring).
Periata Breatta
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.