1. Introducción
Cuando usamos Spring en una aplicación web, tenemos varias opciones para organizar los contextos de la aplicación que lo conectan todo.
En este artículo vamos a analizar y explicar las opciones más habituales que ofrece Spring.
2. El contexto de la aplicación web raíz
Cada aplicación web de Spring tiene un contexto de aplicación asociado que está vinculado a su ciclo de vida: el contexto de la aplicación web raíz.
Esta es una característica antigua que es anterior a Spring Web MVC, por lo que no está vinculada específicamente a ninguna tecnología de marco web.
El contexto se inicia cuando se inicia la aplicación y se destruye cuando se detiene, gracias a un detector de contexto de servlet. Los tipos más comunes de contextos también se pueden actualizar en tiempo de ejecución, aunque no todas las implementaciones de ApplicationContext tienen esta capacidad.
El contexto de una aplicación web es siempre una instancia de WebApplicationContext . Esa es una interfaz que extiende ApplicationContext con un contrato para acceder a ServletContext .
De todos modos, las aplicaciones generalmente no deberían preocuparse por esos detalles de implementación: el contexto de la aplicación web raíz es simplemente un lugar centralizado para definir beans compartidos.
2.1. El ContextLoaderListener
El contexto de la aplicación web raíz descrito en la sección anterior es administrado por un oyente de la clase org.springframework.web.context.ContextLoaderListener , que es parte del módulo spring-web .
De forma predeterminada, el oyente cargará un contexto de aplicación XML desde /WEB-INF/applicationContext.xml . Sin embargo, esos valores predeterminados se pueden cambiar. Podemos usar anotaciones Java en lugar de XML, por ejemplo.
Podemos configurar este oyente en el descriptor de la aplicación web ( archivo web.xml ) o mediante programación en entornos Servlet 3.x.
En las siguientes secciones, veremos cada una de estas opciones en detalle.
2.2. Usando web.xml y un contexto de aplicación XML
Cuando usamos web.xml , configuramos el oyente como de costumbre:
org.springframework.web.context.ContextLoaderListener
Podemos especificar una ubicación alternativa de la configuración del contexto XML con el parámetro contextConfigLocation :
contextConfigLocation /WEB-INF/rootApplicationContext.xml
O más de una ubicación, separados por comas:
contextConfigLocation /WEB-INF/context1.xml, /WEB-INF/context2.xml
Incluso podemos usar patrones:
contextConfigLocation /WEB-INF/*-context.xml
En cualquier caso, solo se define un contexto, combinando todas las definiciones de frijol cargadas desde las ubicaciones especificadas.
2.3. Usando web.xml y un contexto de aplicación Java
También podemos especificar otros tipos de contextos además del predeterminado basado en XML. Veamos, por ejemplo, cómo usar la configuración de anotaciones de Java en su lugar.
Usamos el parámetro contextClass para decirle al oyente qué tipo de contexto instanciar:
contextClass org.springframework.web.context.support.AnnotationConfigWebApplicationContext
Cada tipo de contexto puede tener una ubicación de configuración predeterminada. En nuestro caso, el AnnotationConfigWebApplicationContext no tiene uno, por lo que tenemos que proporcionarlo.
Por lo tanto, podemos enumerar una o más clases anotadas:
contextConfigLocation com.baeldung.contexts.config.RootApplicationConfig, com.baeldung.contexts.config.NormalWebAppConfig
O podemos decirle al contexto que escanee uno o más paquetes:
contextConfigLocation com.baeldung.bean.config
Y, por supuesto, podemos mezclar y combinar las dos opciones.
2.4. Configuración programática con Servlet 3.x
La versión 3 de la API de Servlet ha hecho que la configuración a través del archivo web.xml sea completamente opcional. Las bibliotecas pueden proporcionar sus fragmentos web, que son piezas de configuración XML que pueden registrar oyentes, filtros, servlets, etc.
Además, los usuarios tienen acceso a una API que permite definir programáticamente cada elemento de una aplicación basada en servlets.
El módulo spring-web hace uso de estas características y ofrece su API para registrar componentes de la aplicación cuando se inicia.
Spring escanea la ruta de clases de la aplicación en busca de instancias de la clase org.springframework.web.WebApplicationInitializer . Esta es una interfaz con un solo método, void onStartup (ServletContext servletContext) lanza ServletException , que se invoca al inicio de la aplicación.
Veamos ahora cómo podemos usar esta función para crear los mismos tipos de contextos de aplicaciones web raíz que hemos visto anteriormente.
2.5. Usando Servlet 3.xy un contexto de aplicación XML
Comencemos con un contexto XML, como en la Sección 2.2.
Implementaremos el método onStartup mencionado anteriormente :
public class ApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { //... } }
Analicemos la implementación línea por línea.
Primero creamos un contexto raíz. Como queremos usar XML, tiene que ser un contexto de aplicación basado en XML, y como estamos en un entorno web, también tiene que implementar WebApplicationContext .
La primera línea, por lo tanto, es la versión explícita del parámetro contextClass que hemos encontrado anteriormente, con el que decidimos qué implementación de contexto específico usar:
XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
Luego, en la segunda línea, le indicamos al contexto desde dónde cargar sus definiciones de bean. Nuevamente, setConfigLocations es el análogo programático del parámetro contextConfigLocation en web.xml :
rootContext.setConfigLocations("/WEB-INF/rootApplicationContext.xml");
Finalmente, creamos un ContextLoaderListener con el contexto raíz y lo registramos con el contenedor de servlets. Como podemos ver, ContextLoaderListener tiene un constructor apropiado que toma un WebApplicationContext y lo pone a disposición de la aplicación:
servletContext.addListener(new ContextLoaderListener(rootContext));
2.6. Usando Servlet 3.xy un contexto de aplicación Java
Si queremos utilizar un contexto basado en anotaciones, podríamos cambiar el fragmento de código de la sección anterior para que, en su lugar, cree una instancia de AnnotationConfigWebApplicationContext .
Sin embargo, veamos un enfoque más especializado para obtener el mismo resultado.
The WebApplicationInitializer class that we've seen earlier is a general-purpose interface. It turns out that Spring provides a few more specific implementations, including an abstract class called AbstractContextLoaderInitializer.
Its job, as the name implies, is to create a ContextLoaderListener and register it with the servlet container.
We only have to tell it how to build the root context:
public class AnnotationsBasedApplicationInitializer extends AbstractContextLoaderInitializer { @Override protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); rootContext.register(RootApplicationConfig.class); return rootContext; } }
Here we can see that we no longer need to register the ContextLoaderListener, which saves us from a little bit of boilerplate code.
Note also the use of the register method that is specific to AnnotationConfigWebApplicationContext instead of the more generic setConfigLocations: by invoking it, we can register individual @Configuration annotated classes with the context, thus avoiding package scanning.
3. Dispatcher Servlet Contexts
Let's now focus on another type of application context. This time, we'll be referring to a feature which is specific to Spring MVC, rather than part of Spring's generic web application support.
Spring MVC applications have at least one Dispatcher Servlet configured (but possibly more than one, we'll talk about that case later). This is the servlet that receives incoming requests, dispatches them to the appropriate controller method, and returns the view.
Each DispatcherServlet has an associated application context. Beans defined in such contexts configure the servlet and define MVC objects like controllers and view resolvers.
Let's see how to configure the servlet's context first. We'll look at some in-depth details later.
3.1. Using web.xml and an XML Application Context
DispatcherServlet is typically declared in web.xml with a name and a mapping:
normal-webapp org.springframework.web.servlet.DispatcherServlet 1 normal-webapp /api/*
If not otherwise specified, the name of the servlet is used to determine the XML file to load. In our example, we'll use the file WEB-INF/normal-webapp-servlet.xml.
We can also specify one or more paths to XML files, in a similar fashion to ContextLoaderListener:
... contextConfigLocation /WEB-INF/normal/*.xml
3.2. Using web.xml and a Java Application Context
When we want to use a different type of context we proceed like with ContextLoaderListener, again. That is, we specify a contextClass parameter along with a suitable contextConfigLocation:
normal-webapp-annotations org.springframework.web.servlet.DispatcherServlet contextClass org.springframework.web.context.support.AnnotationConfigWebApplicationContext contextConfigLocation com.baeldung.contexts.config.NormalWebAppConfig 1
3.3. Using Servlet 3.x and an XML Application Context
Again, we'll look at two different methods for programmatically declaring a DispatcherServlet, and we'll apply one to an XML context and the other to a Java context.
So, let's start with a generic WebApplicationInitializer and an XML application context.
As we've seen previously, we have to implement the onStartup method. However, this time we'll create and register a dispatcher servlet, too:
XmlWebApplicationContext normalWebAppContext = new XmlWebApplicationContext(); normalWebAppContext.setConfigLocation("/WEB-INF/normal-webapp-servlet.xml"); ServletRegistration.Dynamic normal = servletContext.addServlet("normal-webapp", new DispatcherServlet(normalWebAppContext)); normal.setLoadOnStartup(1); normal.addMapping("/api/*");
We can easily draw a parallel between the above code and the equivalent web.xml configuration elements.
3.4. Using Servlet 3.x and a Java Application Context
This time, we'll configure an annotations-based context using a specialized implementation of WebApplicationInitializer: AbstractDispatcherServletInitializer.
That's an abstract class that, besides creating a root web application context as previously seen, allows us to register one dispatcher servlet with minimum boilerplate:
@Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext secureWebAppContext = new AnnotationConfigWebApplicationContext(); secureWebAppContext.register(SecureWebAppConfig.class); return secureWebAppContext; } @Override protected String[] getServletMappings() { return new String[] { "/s/api/*" }; }
Here we can see a method for creating the context associated with the servlet, exactly like we've seen before for the root context. Also, we have a method to specify the servlet's mappings, as in web.xml.
4. Parent and Child Contexts
So far, we've seen two major types of contexts: the root web application context and the dispatcher servlet contexts. Then, we might have a question: are those contexts related?
It turns out that yes, they are. In fact, the root context is the parent of every dispatcher servlet context. Thus, beans defined in the root web application context are visible to each dispatcher servlet context, but not vice versa.
So, typically, the root context is used to define service beans, while the dispatcher context contains those beans that are specifically related to MVC.
Note that we've also seen ways to create the dispatcher servlet context programmatically. If we manually set its parent, then Spring does not override our decision, and this section no longer applies.
In simpler MVC applications, it's sufficient to have a single context associated to the only one dispatcher servlet. There's no need for overly complex solutions!
Still, the parent-child relationship becomes useful when we have multiple dispatcher servlets configured. But when should we bother to have more than one?
In general, we declare multiple dispatcher servlets when we need multiple sets of MVC configuration. For example, we may have a REST API alongside a traditional MVC application or an unsecured and a secure section of a website:

Note: when we extend AbstractDispatcherServletInitializer (see section 3.4), we register both a root web application context and a single dispatcher servlet.
So, if we want more than one servlet, we need multiple AbstractDispatcherServletInitializer implementations. However, we can only define one root context, or the application won't start.
Fortunately, the createRootApplicationContext method can return null. Thus, we can have one AbstractContextLoaderInitializer and many AbstractDispatcherServletInitializer implementations that don't create a root context. In such a scenario, it is advisable to order the initializers with @Order explicitly.
Also, note that AbstractDispatcherServletInitializer registers the servlet under a given name (dispatcher) and, of course, we cannot have multiple servlets with the same name. So, we need to override getServletName:
@Override protected String getServletName() { return "another-dispatcher"; }
5. A Parent and Child Context Example
Suppose that we have two areas of our application, for example a public one which is world accessible and a secured one, with different MVC configurations. Here, we'll just define two controllers that output a different message.
Also, suppose that some of the controllers need a service that holds significant resources; a ubiquitous case is persistence. Then, we'll want to instantiate that service only once, to avoid doubling its resource usage, and because we believe in the Don't Repeat Yourself principle!
Let's now proceed with the example.
5.1. The Shared Service
In our hello world example, we settled for a simpler greeter service instead of persistence:
package com.baeldung.contexts.services; @Service public class GreeterService { @Resource private Greeting greeting; public String greet() { return greeting.getMessage(); } }
We'll declare the service in the root web application context, using component scanning:
@Configuration @ComponentScan(basePackages = { "com.baeldung.contexts.services" }) public class RootApplicationConfig { //... }
We might prefer XML instead:
5.2. The Controllers
Let's define two simple controllers which use the service and output a greeting:
package com.baeldung.contexts.normal; @Controller public class HelloWorldController { @Autowired private GreeterService greeterService; @RequestMapping(path = "/welcome") public ModelAndView helloWorld() { String message = "Normal " + greeterService.greet() + "
"; return new ModelAndView("welcome", "message", message); } } //"Secure" Controller package com.baeldung.contexts.secure; String message = "Secure " + greeterService.greet() + "
";
As we can see, the controllers lie in two different packages and print different messages: one says “normal”, the other “secure”.
5.3. The Dispatcher Servlet Contexts
As we said earlier, we're going to have two different dispatcher servlet contexts, one for each controller. So, let's define them, in Java:
//Normal context @Configuration @EnableWebMvc @ComponentScan(basePackages = { "com.baeldung.contexts.normal" }) public class NormalWebAppConfig implements WebMvcConfigurer { //... } //"Secure" context @Configuration @EnableWebMvc @ComponentScan(basePackages = { "com.baeldung.contexts.secure" }) public class SecureWebAppConfig implements WebMvcConfigurer { //... }
Or, if we prefer, in XML:
5.4. Putting It All Together
Now that we have all the pieces, we just need to tell Spring to wire them up. Recall that we need to load the root context and define the two dispatcher servlets. Although we've seen multiple ways to do that, we'll now focus on two scenarios, a Java one and an XML one. Let's start with Java.
We'll define an AbstractContextLoaderInitializer to load the root context:
@Override protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); rootContext.register(RootApplicationConfig.class); return rootContext; }
Then, we need to create the two servlets, thus we'll define two subclasses of AbstractDispatcherServletInitializer. First, the “normal” one:
@Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext normalWebAppContext = new AnnotationConfigWebApplicationContext(); normalWebAppContext.register(NormalWebAppConfig.class); return normalWebAppContext; } @Override protected String[] getServletMappings() { return new String[] { "/api/*" }; } @Override protected String getServletName() { return "normal-dispatcher"; }
Then, the “secure” one, which loads a different context and is mapped to a different path:
@Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext secureWebAppContext = new AnnotationConfigWebApplicationContext(); secureWebAppContext.register(SecureWebAppConfig.class); return secureWebAppContext; } @Override protected String[] getServletMappings() { return new String[] { "/s/api/*" }; } @Override protected String getServletName() { return "secure-dispatcher"; }
And we're done! We've just applied what we touched in previous sections.
We can do the same with web.xml, again just by combining the pieces we've discussed so far.
Define a root application context:
org.springframework.web.context.ContextLoaderListener
A “normal” dispatcher context:
normal-webapp org.springframework.web.servlet.DispatcherServlet 1 normal-webapp /api/*
And, finally, a “secure” context:
secure-webapp org.springframework.web.servlet.DispatcherServlet 1 secure-webapp /s/api/*
6. Combining Multiple Contexts
There are other ways than parent-child to combine multiple configuration locations, to split big contexts and better separate different concerns. We've seen one example already: when we specify contextConfigLocation with multiple paths or packages, Spring builds a single context by combining all the bean definitions, as if they were written in a single XML file or Java class, in order.
However, we can achieve a similar effect with other means and even use different approaches together. Let's examine our options.
One possibility is component scanning, which we explain in another article.
6.1. Importing a Context Into Another
Alternatively, we can have a context definition import another one. Depending on the scenario, we have different kinds of imports.
Importing a @Configuration class in Java:
@Configuration @Import(SomeOtherConfiguration.class) public class Config { ... }
Loading some other type of resource, for example, an XML context definition, in Java:
@Configuration @ImportResource("classpath:basicConfigForPropertiesTwo.xml") public class Config { ... }
Finally, including an XML file in another one:
Thus, we have many ways to organize the services, components, controllers, etc., that collaborate to create our awesome application. And the nice thing is that IDEs understand them all!
7. Spring Boot Web Applications
Spring Boot automatically configures the components of the application, so, generally, there is less need to think about how to organize them.
Still, under the hood, Boot uses Spring features, including those that we've seen so far. Let's see a couple of noteworthy differences.
Spring Boot web applications running in an embedded container don't run any WebApplicationInitializer by design.
Should it be necessary, we can write the same logic in a SpringBootServletInitializer or a ServletContextInitializer instead, depending on the chosen deployment strategy.
However, for adding servlets, filters, and listeners as seen in this article, it is not necessary to do so. In fact, Spring Boot automatically registers every servlet-related bean to the container:
@Bean public Servlet myServlet() { ... }
The objects so defined are mapped according to conventions: filters are automatically mapped to /*, that is, to every request. If we register a single servlet, it is mapped to /, otherwise, each servlet is mapped to its bean name.
Si las convenciones anteriores no nos funcionan, podemos definir FilterRegistrationBean , ServletRegistrationBean o ServletListenerRegistrationBean en su lugar. Esas clases nos permiten controlar los aspectos finos del registro.
8. Conclusiones
En este artículo, brindamos una vista en profundidad de las diversas opciones disponibles para estructurar y organizar una aplicación web Spring.
Hemos omitido algunas características, en particular el soporte para un contexto compartido en aplicaciones empresariales, que, en el momento de redactar este artículo, todavía falta en Spring 5.
La implementación de todos estos ejemplos y fragmentos de código se puede encontrar en el proyecto GitHub.