ContextLoaderListener ou pas?

122

Une application Web Spring standard (créée par Roo ou modèle "Spring MVC Project") crée un fichier web.xml avec ContextLoaderListeneret DispatcherServlet. Pourquoi n'utilisent-ils pas seulement le DispatcherServletet le font-ils pour charger la configuration complète?

Je comprends que le ContextLoaderListener doit être utilisé pour charger les éléments qui ne sont pas pertinents pour le Web et que le DispatcherServlet est utilisé pour charger les éléments pertinents pour le Web (contrôleurs, ...). Et ce résultat dans deux contextes: un contexte parent et un contexte enfant.

Contexte:

Je le faisais de cette manière standard pendant plusieurs années.

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

Cela posait souvent des problèmes avec les deux contextes et les dépendances entre eux. Dans le passé, j'ai toujours pu trouver une solution et j'ai le fort sentiment que cela améliore toujours la structure / l'architecture du logiciel. Mais maintenant je suis confronté à un problème avec les événements des deux contextes .

- Cependant, cela me fait repenser ce modèle à deux contextes, et je me demande: pourquoi devrais-je me retrouver dans ce problème, pourquoi ne pas charger tous les fichiers de configuration de printemps avec un DispatcherServletet supprimer ContextLoaderListenercomplètement le. (Je veux toujours avoir différents fichiers de configuration, mais un seul contexte.)

Y a-t-il une raison de ne pas supprimer le ContextLoaderListener?

Ralph
la source
"Cela posait souvent des problèmes avec les deux contextes et les dépendances entre eux." C'est un excellent exemple de la façon dont je pense que les frameworks d'injection de dépendances rendent nos vies plus difficiles que l'injection de dépendances à faire soi-même.
Andy
1
@Andy - Bien que j'aie une certaine sympathie avec ce point de vue, je ne peux m'empêcher de remarquer que les cas d'utilisation pour lesquels vous avez besoin des deux contextes (partage d'objets entre les filtres de sécurité et les servlets, gestion automatique des transactions afin qu'elles soient fermées après la vue que vous redirigez vers a terminé le rendu) sont assez difficiles à réaliser sans l'aide du framework. Ceci est principalement dû au fait que l'API de servlet n'a clairement jamais été conçue pour fonctionner avec l'injection de dépendances, et travaille activement contre vous si vous essayez de le faire vous-même.
Periata Breatta
@PeriataBreatta je vois! Eh bien, pensez-vous que s'il avait été conçu différemment, il y aurait de meilleures alternatives à Spring MVC? Bien que les gens auraient pu concevoir des alternatives complètes à l'API Servlet de toute façon ...
Andy
@PeriataBreatta Il est intéressant de noter que dans le monde JS, où j'utilise Express pour le routage des requêtes HTTP depuis environ un an, je vois rarement une mention d '"injection de dépendances" et rien ne ressemble du tout au framework Spring.
Andy

Réponses:

86

Dans votre cas, non, il n'y a aucune raison de conserver le ContextLoaderListeneret applicationContext.xml. Si votre application fonctionne correctement avec uniquement le contexte du servlet, cela reste plus simple.

Oui, le modèle généralement recommandé est de conserver les éléments non Web dans le contexte au niveau de l'application Web, mais ce n'est rien de plus qu'une convention faible.

Les seules raisons convaincantes d'utiliser le contexte au niveau de l'application Web sont:

  • Si vous en avez plusieurs DispatcherServletqui doivent partager des services
  • Si vous avez des servlets hérités / non Spring qui ont besoin d'accéder aux services Spring-wired
  • Si vous avez des filtres servlet qui crochet dans le contexte niveau webapp (par exemple de printemps de sécurité DelegatingFilterProxy, OpenEntityManagerInViewFilteretc.)

Aucun de ces éléments ne s'applique à vous, donc la complexité supplémentaire est injustifiée.

Soyez juste prudent lorsque vous ajoutez des tâches d'arrière-plan au contexte du servlet, comme des tâches planifiées, des connexions JMS, etc. Si vous oubliez d'ajouter <load-on-startup>à votre web.xml, ces tâches ne seront pas lancées avant le premier accès au servlet.

skaffman
la source
2
Qu'en est-il des écouteurs, il semble qu'ils ont besoin du contexte créé par l'écouteur du chargeur de contexte (IllegalStateException, Aucun WebApplicationContext trouvé, déclenché par MultipartFilter, CharacterEncodingFilter, HiddenHttpMethodFilter, Spring Security DelegatingFilterProxy et OpenEntityManagerInViewFilter). Est-ce une bonne idée de le faire dans l'autre sens (charger tout par ContextLoaderListener et laisser DispatcherServlet sans configuration)?
Ralph
@Ralph: Bonne prise, j'ai ajouté ce cas d'utilisation à la liste. Quant à partir DispatcherServletsans configuration - si vous faisiez cela, vous n'auriez pas d'interface Web. Tous les trucs MVC doivent y aller.
skaffman
2
@skaffman Pourquoi devrais-je utiliser deux contextes lors de l'utilisation de spring-security avec DelegatingFilterProxy? Dans mon cas, les beans spring-security et le contexte spring par défaut partagent certains beans. Ils devraient donc également partager le même contexte. Ou faut-il garder les beans de sécurité de printemps hors du contexte de printemps par défaut?
Matthias M
10

Vous pouvez également configurer le contexte de l'application dans l'autre sens. Par exemple, pour faire fonctionner OpenEntityManagerInViewFilter . Configurez le ContextLoaderListener , puis configurez votre DispatcherServlet avec:

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

Assurez-vous simplement que la valeur du paramètre contextConfigLocation est vide.

Gunnar Hillert
la source
1
Mais quel est l'avantage de cette configuration? Et qu'entendez-vous par «l'inverse»?
Ralph
La solution de "skaffman" a configuré un contexte d'application Web (servlet) uniquement. Cependant, avec cette approche, vous rencontrez des problèmes tels que détaillés dans la solution elle-même: "Les seules raisons impérieuses d'utiliser le contexte au niveau de l'application web sont:" ... "Si vous avez des filtres de servlet qui se raccordent au contexte au niveau de la webbapp (par exemple DelegatingFilterProxy, OpenEntityManagerInViewFilter, etc. de Spring Security "Si vous souhaitez utiliser un seul fichier XML de contexte d'application, je pense que ma solution (en spécifiant le XML via le ContextLoaderListener) serait préférable.
Gunnar Hillert
pouvez-vous utiliser MVC Web Controller dans le contexte créé par l'écouteur de contexte?
Ralph
1
Oui. Vous configureriez simplement vos contrôleurs dans le fichier context.xml spécifié par l'écouteur de contexte. La façon dont cela fonctionne est que le DispatcherServlet rejoindra simplement le "contexte d'application parent" (Context Listener). Lorsque vous laissez la valeur "contextConfigLocation" vide, le fichier context.xml spécifié par l'écouteur de contexte sera utilisé exclusivement.
Gunnar Hillert
1
Je pense que vous avez manqué <mvc: annotation-driven /> dans votre contexte. La solution @GunnarHillert fonctionne pour moi.
milbr
10

Je souhaite partager ce que j'ai fait sur mon application Spring-MVC:

  1. Sur le we-mvc-config.xmlj'ai ajouté juste les classes annotées avec @Controller:

    <context:component-scan base-package="com.shunra.vcat">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
  2. Sur les applicationContext.xmlfichiers j'ai ajouté tout le reste:

    <context:component-scan base-package="com.shunra.vcat">
        <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
Modi
la source
Oui, c'est un modèle utile. Un autre modèle utile consiste simplement à placer vos beans de gestion de base de données dans le contexte de l'application (ceux-ci sont probablement nécessaires pour un OpenSessionInViewFilter ou similaire) avec tout ce qui est spécifiquement nécessaire aux filtres ou aux écouteurs (par exemple, les définitions nécessaires pour utiliser la sécurité Spring).
Periata Breatta