LuckyLuke LuckyLuke - 6 months ago 56
Java Question

Spring: namespace vs contextConfigLocation init parameters in web.xml

I am reading the documentation for Spring MVC and I have a question regarding the init params. I am using Spring 3.2 if it matter. What is the difference between the contextConfigLocation and namespace? Is the contextConfigLocation meant only for specifying the folders where the context class can find a XML definition and the namespace attribute is meant for specifying the file name?

<servlet>
<servlet-name>AppServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF</param-value>
</init-param>
<init-param>
<param-name>namespace</param-name>
<param-value>application-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>


Is this correct? Should it use /WEB-INF/application-context.xml? And should you specify paths?

Answer

TL;DR

Just set the value(s) for the contextConfigLocation whenever you need to specify custom config files. This way you will be specifying both the config file names and their locations.

The namespace is essentially an alternative way of telling Spring container context loader class what config file to use. I never bother with it, but just use contextConfigLocation whenever I need to configure custom config files.

Here is an example from one of my previous Spring projects (some of the configurations omitted for the sake of brevity):

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                            http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <display-name>Spring Web Application example</display-name>

    <!-- Configurations for the root application context (parent context) -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/jdbc/spring-jdbc.xml
            /WEB-INF/spring/security/spring-security-context.xml
        </param-value>
    </context-param>

    <!-- Configurations for the DispatcherServlet application context (child context) -->
    <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>
                /WEB-INF/spring/mvc/spring-mvc-servlet.xml
            </param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring-mvc</servlet-name>
        <url-pattern>/admin/*</url-pattern>
    </servlet-mapping>

</web-app>

Long answer

OK, first let's get some important moments clear. There are two types of contexts we are dealing with:

  1. root context (parent)
  2. individual servlet context (child)

Quote from the Spring Framework API (version 3.2.2 at the moment of writing this) for the WebApplicationContext (emphasis mine):

Like generic application contexts, web application contexts are hierarchical. There is a single root context per application, while each servlet in the application (including a dispatcher servlet in the MVC framework) has its own child context.

Also here: Context hierarchies:

For example, if you are developing a Spring MVC web application you will typically have a root WebApplicationContext loaded via Spring's ContextLoaderListener and a child WebApplicationContext loaded via Spring's DispatcherServlet. This results in a parent-child context hierarchy where shared components and infrastructure configuration are declared in the root context and consumed in the child context by web-specific components.

And here: 17.2 The DispatcherServlet:

ApplicationContext instances in Spring can be scoped. In the Web MVC framework, each DispatcherServlet has its own WebApplicationContext, which inherits all the beans already defined in the root WebApplicationContext. These inherited beans can be overridden in the servlet-specific scope, and you can define new scope-specific beans local to a given Servlet instance.


Now let's see the root application context configuration. Here is an example:
web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                            http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/daoContext.xml
            /WEB-INF/spring/applicationContext.xml
        </param-value>
    </context-param>
</web-app>


From the official Spring documentation (emphasis mine):
5.14.4 Convenient ApplicationContext instantiation for web applications:

You can create ApplicationContext instances declaratively by using, for example, a ContextLoader. Of course you can also create ApplicationContext instances programmatically by using one of the ApplicationContext implementations.

You can register an ApplicationContext using the ContextLoaderListener (see the example above)

The listener inspects the contextConfigLocation parameter. If the parameter does not exist, the listener uses /WEB-INF/applicationContext.xml as a default. When the parameter does exist, the listener separates the String by using predefined delimiters (comma, semicolon and whitespace) and uses the values as locations where application contexts will be searched. Ant-style path patterns are supported as well. Examples are /WEB-INF/*Context.xml for all files with names ending with "Context.xml", residing in the "WEB-INF" directory, and /WEB-INF/**/*Context.xml, for all such files in any subdirectory of "WEB-INF".


Quite often Spring configuration is split across multiple files. It's more logical and convenient, especially in the large-scale projects. In our example we explicitly defined two configuration XML files: daoContext.xml and applicationContext.xml in the custom location: /WEB-INF/spring/. Again, had we not defined contextConfigLocation, the ContextLoaderListener would try to locate the default config file: /WEB-INF/applicationContext.xml.

NOTE:
The root context is optional. Also see this answer: http://stackoverflow.com/a/7451389/814702

So if the default /WEB-INF/applicationContext.xml config file does not suit your needs, use ContextLoaderListener along with <context-param> contextConfigLocation where you can define custom config file(s) to define root application context.


Next let's see the individual (child) application context. From the official Spring documentation (emphasis mine):
17.2 The DispatcherServlet

Upon initialization of a DispatcherServlet, Spring MVC looks for a file named
[servlet-name]-servlet.xml in the WEB-INF directory
of your web application and creates the beans defined there, overriding the definitions of any beans defined with the same name in the global scope.

Consider the following DispatcherServlet Servlet configuration (in the web.xml file):

<web-app>

    <servlet>
        <servlet-name>golfing</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>golfing</servlet-name>
        <url-pattern>/golfing/*</url-pattern>
    </servlet-mapping>

</web-app>


About contextConfigLocation and namespace

From the documentation (emphasis mine):

With the above Servlet configuration in place, you will need to have a file called
/WEB-INF/golfing-servlet.xml in your application; this file will contain all of your Spring Web MVC-specific components (beans). You can change the exact location of this configuration file through a Servlet initialization parameter (see below for details).
...
You can customize individual DispatcherServlet instances by adding Servlet initialization parameters (init-param elements) to the Servlet declaration in the web.xml file. See the following table for the list of supported parameters.

  • contextClass: Class that implements WebApplicationContext, which instantiates the context used by this Servlet. By default, the XmlWebApplicationContext is used.

  • contextConfigLocation: String that is passed to the context instance (specified by contextClass) to indicate where context(s) can be found. The string consists potentially of multiple strings (using a comma as a delimiter) to support multiple contexts. In case of multiple context locations with beans that are defined twice, the latest location takes precedence.

  • namespace: Namespace of the WebApplicationContext. Defaults to [servlet-name]-servlet.


Now let's research the API documentation for the related classes. The class DispatcherServlet extends abstract class FrameworkServlet. From the FrameworkServlet API docs (emphasis mine):

Passes a "contextConfigLocation" servlet init-param to the context instance, parsing it into potentially multiple file paths which can be separated by any number of commas and spaces, like
"test-servlet.xml, myServlet.xml". If not explicitly specified, the context implementation is supposed to build a default location from the namespace of the servlet.

The default namespace is "'servlet-name'-servlet", e.g. "test-servlet" for a servlet-name "test" (leading to a "/WEB-INF/test-servlet.xml" default location with XmlWebApplicationContext). The namespace can also be set explicitly via the "namespace" servlet init-param.

This is the excerpt from the FrameworkServlet source code:
FrameworkServlet.java

....
/**
* Suffix for WebApplicationContext namespaces. If a servlet of this class is
* given the name "test" in a context, the namespace used by the servlet will
* resolve to "test-servlet".
*/
public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";
....


The default context class for FrameworkServlet is XmlWebApplicationContext. From the XmlWebApplicationContext API docs (emphasis mine):

By default, the configuration will be taken from "/WEB-INF/applicationContext.xml" for the root context, and "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet" (like for a DispatcherServlet instance with the servlet-name "test").

The config location defaults can be overridden via the "contextConfigLocation" context-param of ContextLoader and servlet init-param of FrameworkServlet. Config locations can either denote concrete files like "/WEB-INF/context.xml" or Ant-style patterns like "/WEB-INF/*-context.xml" (see PathMatcher javadoc for pattern details).

Overriding default config locations using contextConfigLocation is the same as in the above example for the root application context.

As for the overriding the default namespace there are some important moments. When you set a new namespace, don't prepend it with /WEB-INF and don't append .xml to it. The reason for that can be discovered if we look in the source file for the XmlWebApplicationContext class:
XmlWebApplicationContext.java

...

/** Default config location for the root context */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

/** Default prefix for building a config location for a namespace */
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";

/** Default suffix for building a config location for a namespace */
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";

...

/**
* The default location for the root context is "/WEB-INF/applicationContext.xml",
* and "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet"
* (like for a DispatcherServlet instance with the servlet-name "test").
*/
@Override
protected String[] getDefaultConfigLocations() {
    if (getNamespace() != null) {
        return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
    }
    else {
        return new String[] {DEFAULT_CONFIG_LOCATION};
    }
}

As you can see, the source code says it all.


Example of specifying custom namespace

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                            http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">


    <!-- Configurations for the DispatcherServlet application context (child context) -->
    <servlet>
        <servlet-name>spring-mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>namespace</param-name>
            <param-value>spring/mvc/spring-mvc</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring-mvc</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

The result is that, instead of using the default namespace for constructing the path to the config file, that would otherwise be /WEB-INF/spring-mvc-servlet.xml, the container will look for /WEB-INF/spring/mvc/spring-mvc.xml.

NOTE:
The above explanations related to the setting custom namespace are for the default XmlWebApplicationContext context class. One can specify an alternative class, like AnnotationConfigWebApplicationContext, so there will be some special moments for that.


CONCLUSION

It is (IMHO) much more easier to use contextConfigLocation parameter to define custom config files, both for the root application context and for the individual contexts. The only difference is that for the root application context you use <context-param> within <web-app> element, but NOT within a specific servlet (also don't forget the listener class). And for the child context you use <init-param> nested inside the <servlet> element for each specific servlet. See my example configurations (web.xml) in the very beginning of this post.

Additional sources (as if the above was not enough :-)):

Also see these answers:

Comments