In this post we will discuss on how to setup/configure the Spring WebMVC application using JavaConfig style and Servlet 3 (without web.xml).

Lets start on the topic,

The Spring WebMVC implements MVC and front controller patterns. The DispatcherServlet is regular Java Servlet from Spring WebMVC API. This is the web request entry point to the Spring WebMVC workflow. The workflow is implemented using the framework objects such as HandlerMapping, HandlerAdapter, and ViewResolver.

Thus the basic Spring WebMVC setup includes configuring the DispatcherServlet and its dependencies (framework objects).

The following 2 steps are involved in setting up the basic Spring WebMVC system:

Step-1: Register the DispatcherServlet to servlet container

Step-2: Define the Spring WebMVC framework objects

Lets proceed to understand how to implement these 2 steps using JavaConfig style

Step-1: Registering the DispatcherServlet

The DispatcherServlet is registered as a Java servlet to the WebContainer so that it can listen for the the web request. Further the web request can be processed by DispatcherServlet accordingly. Traditionally a Java Servlet is registered by defining it in web.xml file as shown in code snippet 1.1:

Code Snippet 1.1

<servlet>
 <servlet-name>ds</servlet-name>
 <servlet-class>org.springframrework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
 <servlet-name>ds</servlet-name>
 <url-pattern>/</url-pattern>
</servlet-mapping>

Background: The Servlet 3.0 introduces a way to programatically customize the servlet container initialization. The  javax.servlet.ServletContainerInitializer is a special interface to implement the custom initialization. The ServletContainerInitializer object is invoked by ServletContainer on web application startup.

The Spring WebMVC 3.1 works on Servlet 3 or higher versions. The   org.springframework.web.SpringServletContainerInitializer class is a built-in implementation of ServletContainerInitializer from Spring WebMVC in support of JavaConfig configuration style. The SpringServletContainerInitializer object handles the classes subtype of  org.springframework.web.WebApplicationInitializer. The SpringSerletContainerInitializer invoking the onStartup() method of all the WebApplicationInitializer objects in the context.

The following two options are available for registering the DispatcherServlet:

Option-1: Implementing WebApplicationInitializer

Option-2: Implementing AbstractAnnotationConfigDispatcherServletInitializer

Option-1: Implementing WebApplicationInitializer

In this approach we want to implement a class subtype of interface   org.springframework.web.WebApplicationInitializer . The onStartUp() method of this object is responsible to initialize and register the DispatcherServlet. The code snipper 1.2 shows sample implementation of WebApplicationInitializer.

Code Snippet 1.2

package com.techexposer.sample.config;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class SpringWebMVCInitializer implements WebApplicationInitializer {

  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {
    
    AnnotationConfigWebApplicationContext ctxt = new AnnotationConfigWebApplicationContext();
    ctxt.register(WebConfig.class);
    ctxt.setServletContext(servletContext);
    ctxt.refresh();
    
    DispatcherServlet ds = new DispatcherServlet(ctxt);
    
    ServletRegistration.Dynamic reg = servletContext.addServlet("ds", ds);
    reg.addMapping("/");
    reg.setLoadOnStartup(1);
  }
}

As shown in this snippet we create a DispatcherServlet, inject the WebApplicationContext (servlet scope context). In this case we used AnnotationConfigWebApplicationContext, alternatively we can use XmlWebApplicationContext or any other implementation.

Note: Most of the times we also need to register the ContextLoaderListener which initializes a root (web app scope) WebApplicationContext. In general we define the service and data access layer beans into root context and controller beans into servlet scope context.

The code snippet 1.3 can be included into onStartup() method of code snippet 1.2 to register the ContextLoaderListener.

Code Snippet 1.3

AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(RootConfig.class);
rootContext.setServletContext(servletContext);
rootContext.refresh();
    
ContextLoaderListener ctxtListener = new ContextLoaderListener(rootContext);
servletContext.addListener(ctxtListener);

Option-2:  Implementing AbstractAnnotationConfigDispatcherServletInitializer

The AbstractAnnotationConfigDispatcherServletInitializer is a built-in implementation of WebApplicationInitializer from Spring WebMVC API. This simplifies implementing the most common requirement of initializing and registering the DispatcherServlet and ContextLoaderListener. This implementation of WebApplicationInitializer is an abstract class. The code for registering the DispatcherServlet and ContextLoaderListener is implemented, and has abstract methods for subclass to define the config classes and url pattern mapping which varies from application to application.

The code snippet 1.4 shows using AbstractAnnotationConfigDispatcherServletInitializer for registering the DispatcherServlet and ContextLoaderListener.

Code Snippet 1.4

package com.techexposer.sample.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class SpringWebMVCInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

  @Override
  protected Class<?>[] getRootConfigClasses() {
    
    return new Class[]{AppConfig.class};
  }

  @Override
  protected Class<?>[] getServletConfigClasses() {
    
    return new Class[]{WebConfig.class};
  }

  @Override
  protected String[] getServletMappings() {
    
    return new String[]{"/"};
  }

}

Conclusion: The option-1 is a more generic approach which allows us to play with the ServletContext not only registering the DispatcherServlet and ContextLoaderListener but add custom filters etc. Where as the option-2 is a convenience approach meeting the basic requirement. If we just want to register the Spring WebMVC servlet and listener can prefer Option-2.

Step-2: Defining the Spring WebMVC framework objects

In addition of registering the DispatcherServlet to ServletContext, we need to define some framework objects (possibly as spring beans) such as HandlerMapping and HandlerAdapter that can support the DispatcherServlet for implementing the WebMVC workflow handling the client request.

In case of using XML based definition we use spring mvc namespace tags like  <mvc:annotation-driven/>

The following two options can be used for defining the Spring WebMVC framework objects in JavaConfig style:

Option-1: Using @EnableWebMvc annotation

Option-2: Implementing WebMvcConfigurationSupport

Option-1: Using @EnableWebMvc annotation

The simplest option for defining all the most commonly required framework objects for Spring WebMVC is using @EnableWebMvc annotation.

We want to simply add this annotation to the configuration class i.e. class annotated with @Configuration.

The code snippet 1.5 below shows using this annotation:

Code Snippet 1.5

package com.techexposer.sample.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@EnableWebMvc
@Configuration
@ComponentScan(basePackages={"com.techexposer.sample.controller", "com.techexposer.sample.web"})
public class WebConfig {

}

The @EnableWebMvc annotation imports  org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration configuration class which defines the framework objects, the below listed are few of such objects:

  • RequestMappingHandlerMapping

  • ContentNegotiationManager

  • BeanNameUrlHandlerMapping

  • ResourceUrlProvider

  • RequestMappingHandlerAdapter

  • Validator

  • HttpRequestHandlerAdapter

  • SimpleControllerHandlerAdapter

  • HandlerExceptionResolver

  • ViewResolver’s

The DelegatingWebMvcConfiguration supports customization such as adding interceptors. We can implement  org.springframework.web.servlet.config.annotation.WebMvcConfigurer and override the required default methods to define the customizations. The code snippet 1.6 shows adding interceptor.

Code Snippet 1.6

package com.techexposer.sample.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.santoshkoona.learning.controller.interceptors.LoggingInterceptor;

@EnableWebMvc
@Configuration
@ComponentScan(basePackages={"com.techexposer.sample.controller", "com.techexposer.sample.web"})
public class WebConfig implements WebMvcConfigurer {

  @Autowired
  private LoggingInterceptor loggingInterceptor;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    
    InterceptorRegistration ir = registry.addInterceptor(loggingInterceptor);
    ir.addPathPatterns("/*");
    ir.excludePathPatterns("/index");
  }
}

Option-2: Implementing WebMvcConfigurationSupport

In this case we create our web config class subtype of WebMvcConfigurationSupport so that the bean definitions discussed in the above section will be inherited to our web configuration class. The code snippet 1.7 shows using this approach.

Code Snippet 1.7

package com.techexposer.sample.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import com.santoshkoona.learning.controller.interceptors.LoggingInterceptor;

@Configuration
@ComponentScan(basePackages={"com.techexposer.sample.controller", "com.techexposer.sample.web"})
public class WebConfig extends WebMvcConfigurationSupport{
  
  @Autowired
  private LoggingInterceptor loggingInterceptor;

  @Override
  protected void addInterceptors(InterceptorRegistry registry) {
    InterceptorRegistration ir = registry.addInterceptor(loggingInterceptor);
    ir.addPathPatterns("/*");
    ir.excludePathPatterns("/index");
  }	
}

You can download the working sample from the following URL.

 

https://github.com/koonasantosh/spring_samples/tree/master/webmvc-javaconfig-basic

 

Happy Coding 🙂