Introduction to Spring MVC HandlerInterceptor

1. Introduction

In this tutorial we’ll focus on understanding the Spring MVC HandlerInterceptor and how to use it correctly.

2. Spring MVC Handler

In order to understand the interceptor, let’s take a step back and look at the HandlerMapping. This maps a method to a URL, so that the DispatcherServlet will be able to invoke it when processing a request.

And the DispatcherServlet uses the HandlerAdapter to actually invoke the method.

Now that we understand the overall context – this is where the handler interceptor comes in. We’ll use the HandlerInterceptor to perform actions before handling, after handling, or after completion (when the view is rendered), of a request.

The interceptor can be used for cross-cutting concerns and to avoid repetitive handler code like logging, changing globally used parameters in Spring model etc.

In the next few sections that’s exactly what we’re going to be looking at – the differences between various interceptor implementations.

3. Maven Dependencies

In order to use Interceptors, you need to include the following section in a dependencies section of your pom.xml file:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

The latest version can be found here.

4. Spring Handler Interceptor

Interceptors working with the HandlerMapping on the framework must implement the HandlerInterceptor interface.

This interface contains three main methods:

  • prehandle() – called before the actual handler is executed, but the view is not generated yet
  • postHandle() – called after the handler is executed
  • afterCompletion() – called after the complete request has finished and view was generated

These three methods provide flexibility to do all kinds of pre- and post-processing.

And a quick note – the main difference between HandlerInterceptor and HandlerInterceptorAdapter is that in the first one we need to override all three methods: preHandle()postHandle() and afterCompletion(), whereas in the second we may implement only required methods.

A quick note before we go further – if you want to skip the theory and jump straight to examples, jump right into section 5.

Here’s what a simple preHandle() implementation will look like:

@Override
public boolean preHandle(
  HttpServletRequest request,
  HttpServletResponse response, 
  Object handler) throws Exception {
    // your code
    return true;
}

Notice the method returns a boolean value – which tells Spring if the request should be further processed by a handler (true) or not (false).

Next, we have an implementation of postHandle():

@Override
public void postHandle(
  HttpServletRequest request, 
  HttpServletResponse response,
  Object handler, 
  ModelAndView modelAndView) throws Exception {
    // your code
}

This method is called immediately after the request is processed by HandlerAdapter, but before generating a view.

And it can of course be used in many ways – for example, we may add an avatar of a logged-in user into a model.

The final method we need to implement in the custom HandlerInterceptor implementation is afterCompletion():

@Override
public void afterCompletion(
  HttpServletRequest request, 
  HttpServletResponse response,
  Object handler, Exception ex) {
    // your code
}

When the view is successfully generated, we can use this hook to do things like gather additional statistics related to the request.

A final note to remember is that a HandlerInterceptor is registered to the DefaultAnnotationHandlerMapping bean, which is responsible for applying interceptors to any class marked with a @Controller annotation. Moreover, you may specify any number of interceptors in your web application.

5. Custom Logger Interceptor

In this example we will focus on logging in our web application. First of all, our class needs to extend HandlerInterceptorAdapter:

public class LoggerInterceptor extends HandlerInterceptorAdapter {
    ...
}

We also need to enable logging in our interceptor:

private static Logger log = LoggerFactory.getLogger(LoggerInterceptor.class);

This allows Log4J to display logs, as well as indicate, which class is currently logging information to the specified output.

Next, let’s focus on custom interceptor implementations:

5.1. Method preHandle()

This method is called before handling a request; it returns true, to allow the framework to send the request further to the handler method (or to the next interceptor). If the method returns false, Spring assumes that request has been handled and no further processing is needed.

We can use the hook to log information about the requests’ parameters: where the request comes from, etc.

In our example, we are logging this info using a simple Log4J logger:

@Override
public boolean preHandle(
  HttpServletRequest request,
  HttpServletResponse response, 
  Object handler) throws Exception {
    
    log.info("[preHandle][" + request + "]" + "[" + request.getMethod()
      + "]" + request.getRequestURI() + getParameters(request));
    
    return true;
}

As we can see, we’re logging some basic information about the request.

In case we run into a password here, we’ll need to make sure we don’t log that of course.

A simple option is to replace passwords, and any other sensitive type of data, with stars.

Here’s a quick implementation of how that can be done:

private String getParameters(HttpServletRequest request) {
    StringBuffer posted = new StringBuffer();
    Enumeration<?> e = request.getParameterNames();
    if (e != null) {
        posted.append("?");
    }
    while (e.hasMoreElements()) {
        if (posted.length() > 1) {
            posted.append("&");
        }
        String curr = (String) e.nextElement();
        posted.append(curr + "=");
        if (curr.contains("password") 
          || curr.contains("pass")
          || curr.contains("pwd")) {
            posted.append("*****");
        } else {
            posted.append(request.getParameter(curr));
        }
    }
    String ip = request.getHeader("X-FORWARDED-FOR");
    String ipAddr = (ip == null) ? getRemoteAddr(request) : ip;
    if (ipAddr!=null && !ipAddr.equals("")) {
        posted.append("&_psip=" + ipAddr); 
    }
    return posted.toString();
}

Finally, we’re aiming to get the source IP address of the HTTP request.

Here’s a simple implementation:

private String getRemoteAddr(HttpServletRequest request) {
    String ipFromHeader = request.getHeader("X-FORWARDED-FOR");
    if (ipFromHeader != null && ipFromHeader.length() > 0) {
        log.debug("ip from proxy - X-FORWARDED-FOR : " + ipFromHeader);
        return ipFromHeader;
    }
    return request.getRemoteAddr();
}

5.2. Method postHandle()

This hook runs when the HandlerAdapter is invoked the handler but DispatcherServlet is yet to render the view.

We can use this method to add additional attributes to the ModelAndView or to determine the time taken by handler method to process a client’s request.

In our case, we simply log a request just before DispatcherServlet is going to render a view.

@Override
public void postHandle(
  HttpServletRequest request, 
  HttpServletResponse response,
  Object handler, 
  ModelAndView modelAndView) throws Exception {
    
    log.info("[postHandle][" + request + "]");
}

5.3. Method afterCompletion()

When a request is finished and the view is rendered, we may obtain request and response data, as well as information about exceptions, if any occurred:

@Override
public void afterCompletion(
  HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) 
  throws Exception {
    if (ex != null){
        ex.printStackTrace();
    }
    log.info("[afterCompletion][" + request + "][exception: " + ex + "]");
}

6. Configuration

To add our interceptors into Spring configuration, we need to override addInterceptors() method inside WebConfig class that implements WebMvcConfigurer:

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LoggerInterceptor());
}

We may achieve the same configuration by editing our XML Spring configuration file:

<mvc:interceptors>
    <bean id="loggerInterceptor" class="com.maixuanviet.web.interceptor.LoggerInterceptor"/>
</mvc:interceptors>

With this configuration active, the interceptor will be active and all requests in the application will be properly logged.

Please notice, if multiple Spring interceptors are configured, the preHandle() method is executed in the order of configuration, whereas postHandle() and afterCompletion() methods are invoked in the reverse order.

If we’re using Spring Boot instead of vanilla Spring, we should keep in mind to not annotate our configuration class with @EnableWebMvc, or we’ll lose out on Boot’s auto configurations.

7. Conclusion

This tutorial is a quick introduction to intercepting HTTP requests using Spring MVC Handler Interceptor.

All examples and configurations are available here on GitHub.

Related posts:

Java Program to Implement ConcurrentLinkedQueue API
Giới thiệu Google Guice – Binding
Java – Random Long, Float, Integer and Double
Performance Difference Between save() and saveAll() in Spring Data
Spring MVC + Thymeleaf 3.0: New Features
Custom Error Pages with Spring MVC
Implementing a Binary Tree in Java
Java Program to Implement Dijkstra’s Algorithm using Priority Queue
Java Program to Implement Ternary Heap
ETL with Spring Cloud Data Flow
Java Program to Use Boruvka’s Algorithm to Find the Minimum Spanning Tree
Transactions with Spring and JPA
So sánh ArrayList và LinkedList trong Java
Java Program to Perform Postorder Recursive Traversal of a Given Binary Tree
How To Serialize and Deserialize Enums with Jackson
Java – Get Random Item/Element From a List
Java Program to Implement Leftist Heap
Quick Guide to @RestClientTest in Spring Boot
Java Program to Perform integer Partition for a Specific Case
Remove All Occurrences of a Specific Value from a List
Anonymous Classes in Java
Java Program to Implement CopyOnWriteArraySet API
Tìm hiểu cơ chế Lazy Evaluation của Stream trong Java 8
Lớp Arrarys trong Java (Arrays Utility Class)
Spring Boot - CORS Support
Collect a Java Stream to an Immutable Collection
Java Program to Implement Expression Tree
Java Program to Implement Merge Sort on n Numbers Without tail-recursion
Biến trong java
Apache Commons Collections BidiMap
Spring REST API + OAuth2 + Angular (using the Spring Security OAuth legacy stack)
ClassNotFoundException vs NoClassDefFoundError