ContextLoaderListener or not?
Asked Answered
A

3

121

A standard spring web application (created by Roo or "Spring MVC Project" Template) create a web.xml with ContextLoaderListener and DispatcherServlet. Why do they not only use the DispatcherServlet and make it to load the complete configuration?

I understand that the ContextLoaderListener should be used to load the stuff that is not web relevant and the DispatcherServlet is used to load the web relevant stuff (Controllers,...). And this result in two contexts: a parent and a child context.

Background:

I was doing it this standard way for several years.

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

This often caused problems with the two contexts and the dependencies between them. In the past I was always able to find a solution, and I have the strong feeling that this makes the software structure/architecture always better. But now I am facing a problem with the events of the both contexts.

-- However this makes my rethink this two context pattern, and I am asking myself: why should I bring myself into this trouble, why not loading all spring configuration files with one DispatcherServlet and removing the ContextLoaderListener completely. (I still will to have different configuration files, but only one context.)

Is there any reason not to remove the ContextLoaderListener?

Ablaut answered 26/1, 2012 at 9:35 Comment(4)
"This often caused problems with the two contexts and the dependencies between them." This is a great example of how I think dependency injection frameworks just make our lives harder than do-it-yourself dependency injection.Willett
@Willett - While I have some sympathy with this point of view, I can't help notice that the use cases for which you need both contexts (sharing objects between security filters and servlets, automatically managing transactions so they're closed after the view that you redirect to has finished rendering) are quite difficult to achieve without the help of the framework. This is mostly because the servlet API was clearly never designed to work with dependency injection at all, and actively works against you if you try to do it yourself.Iraidairan
@PeriataBreatta I see! Well, do you think if it had been designed differently that there would be better alternatives to Spring MVC? Though people could have designed complete alternatives to the Servlet API anyway...Willett
@PeriataBreatta It's interesting to note that in the JS world, where I've been using Express for routing HTTP requests for around a year, I rarely see any mention of "dependency injection" and nothing resembling the Spring framework at all.Willett
B
90

In your case, no, there's no reason to keep the ContextLoaderListener and applicationContext.xml. If your app works fine with just the servlet's context, that stick with that, it's simpler.

Yes, the generally-encouraged pattern is to keep non-web stuff in the webapp-level context, but it's nothing more than a weak convention.

The only compelling reasons to use the webapp-level context are:

  • If you have multiple DispatcherServlet that need to share services
  • If you have legacy/non-Spring servlets that need access to Spring-wired services
  • If you have servlet filters that hook into the webapp-level context (e.g. Spring Security's DelegatingFilterProxy, OpenEntityManagerInViewFilter, etc)

None of these apply to you, so the extra complexity is unwarranted.

Just be careful when adding background tasks to the servlet's context, like scheduled tasks, JMS connections, etc. If you forget to add <load-on-startup> to your web.xml, then these tasks won't be started until the first access of the servlet.

Bistort answered 26/1, 2012 at 9:41 Comment(3)
What about listeners, it seams that they need the Context create by Context Loader listener (IllegalStateException, No WebApplicationContext found, triggered by MultipartFilter, CharacterEncodingFilter, HiddenHttpMethodFilter, Spring Security DelegatingFilterProxy and OpenEntityManagerInViewFilter). Is it a good idea to do it the other way around (Load every thing by ContextLoaderListener, and leave the DispatcherServlet without a configuration)?Ablaut
@Ralph: Good catch, I've added that use case to the list. As for leaving DispatcherServlet without a configuration - if you did that, you'd have no web interface. All MVC stuff has to go in there.Bistort
@Bistort Why should I use two contexts when using spring-security with DelegatingFilterProxy? In my case spring-security beans and the default spring context share some beans. So they also should share the same context. Or should spring security beans kept out of the default spring context?Telegram
J
10

I want to share what I've done on my Spring-MVC application:

  1. On the we-mvc-config.xml I added just the classes annotated with @Controller:

    <context:component-scan base-package="com.shunra.vcat">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
    
  2. On the applicationContext.xml files I added all the rest:

    <context:component-scan base-package="com.shunra.vcat">
        <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
    
Jordanna answered 25/12, 2012 at 16:13 Comment(1)
Yes, this is a useful pattern. Another useful pattern is just putting your database handling beans in the application context (these are likely to be needed for an OpenSessionInViewFilter or similar) along with anything specifically needed by filters or listeners (e.g. definitions necessary to use spring security).Iraidairan
B
10

You can configure the application context the other way around as well. E.g. in order to make the OpenEntityManagerInViewFilter work. Setup the ContextLoaderListener and then configure your DispatcherServlet with:

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

Just make sure that the contextConfigLocation parameter value is empty.

Boart answered 27/11, 2013 at 21:12 Comment(6)
But what is the advantage of this configuration? And what do you mean by "the other way around"?Ablaut
The solution by "skaffman" configured a web application context (servlet) only. However, with that approach you run into issues as detailed in the solution itself: "The only compelling reasons to use the webapp-level context are:" ... "If you have servlet filters that hook into the webbapp-level context (e.g. Spring Security's DelegatingFilterProxy, OpenEntityManagerInViewFilter, etc)" If you like to use 1 application context XML file only, I think my solution (specifying the XML via the ContextLoaderListener) would be preferable.Boart
are you able to use MVC Web Controller in the Context created by the Context Listener?Ablaut
Yes. You would simply setup your controllers in the context.xml file specified by the Context Listener. The way it works is that the DispatcherServlet will simply join the "parent application context" (Context Listener). As you leave the "contextConfigLocation" value empty the context.xml file specified by the Context Listener will be used exclusively.Boart
Although this is an old comment, just wanted to chip in my observation. @GunnarHillert the method of configuration you suggested did not work right away with Spring MVC. Yes the controller was created as a bean, but the mapping was not set. It only worked after I created a bean of org.springframework.web.servlet.handler.SimpleUrlHandlerMapping and then supplied the mappings. I am not a Spring Master but I would say the reason why this is the case is because the DispathlerServlet not only takes the context and creates beans, it also make sure MVC related functionality (like mapping) is doneContretemps
I think you missed <mvc:annotation-driven/> in your context. @GunnarHillert solution works for me.Redblooded

© 2022 - 2024 — McMap. All rights reserved.