Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Spring MVC Interceptor Implementation and Configuration Guide

Tech May 14 1

Understanding Spring MVC Interceptors

When building web applications with traditional JavaEE filters, developers often encounter a limitation. Filters execute before the Servlet layer, which means with Spring MVC's single entry point (DispatcherServlet), a filter would intercept all incoming requests indiscriminately. Spring MVC Interceptors solve this problem by providing more granular control over request handling within the Spring MVC framework itself.

Interceptor vs Filter: Key Differences

Understanding the distinction between these two components is crucial for proper architecture decisions:

Aspect Interceptor Filter
Framework Spring MVC specific Servlet specification
Initialization Spring IoC container Servlet container
Scope Action requests only Nearly all requests
Context Access Full access to acsion context and value stack Cannot access action context
Lifecycle Calls Multiple invocations per action Single initialization call
Bean Access Direct access to Spring beans via IoC Requires manual service lookup

Creating a Custom Interceptor

To implement an interceptor, extend the HandlerInterceptorAdapter class (a convenient abstract class implementing HandlerInterceptor) or directly implement the HandlerInterceptor interface.

package com.example.mvc.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class AuthInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
                             Object handler) throws Exception {
        System.out.println("AuthInterceptor: Processing before controller execution");
        
        // Example: Check for authentication token
        String authToken = request.getHeader("Authorization");
        if (authToken == null || authToken.isEmpty()) {
            response.sendRedirect("/login");
            return false;
        }
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                          Object handler, ModelAndView modelView) throws Exception {
        System.out.println("AuthInterceptor: Processing after handler, before view rendering");
        
        if (modelView != null) {
            modelView.getModel().put("processedBy", "AuthInterceptor");
        }
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) throws Exception {
        System.out.println("AuthInterceptor: Final cleanup after complete request processing");
    }
}

Spring MVC Configuration

Register the interceptor in your Spring MVC configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- Interceptor registration -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/api/**"/>
            <mvc:mapping path="/secure/*"/>
            <mvc:exclude-mapping path="/api/public/*"/>
            <bean class="com.example.mvc.interceptor.AuthInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

</beans>

Deep Dive into Interceptor Lifecycle Methods

The preHandle Method

This method executes before the controller's handler method is invoked. Use it for:

  • Authentication and authorization checks
  • Request preprocessing (encoding, parameter validation)
  • Early returns to prevent unnecessary controller execution

Parameters:

  • request: The incoming HTTP request
  • response: The HTTP response object
  • handler: The HandlerMethod object representing the target controller method

Return value semantics:

  • true: Allows the request to proceed to the next interceptor or handler
  • false: Blocks request processing (no subsequent interceptors or handlers execute)
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
                         Object handler) throws Exception {
    System.out.println("preHandle triggered for: " + request.getRequestURI());
    
    // Check user session
    HttpSession session = request.getSession(false);
    UserSession userSession = (session != null) ? 
        (UserSession) session.getAttribute("userSession") : null;
    
    if (userSession == null || !userSession.isAuthenticated()) {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getWriter().write("{\"error\": \"Authentication required\"}");
        return false;
    }
    return true;
}

The postHandle Method

This method executes after the handler method completes but before the view is rendered. Use it for:

  • Modifying the ModelAndView object
  • Adding request attributes
  • Changing view names or controlling view logic
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                       Object handler, ModelAndView modelView) throws Exception {
    if (modelView != null) {
        Map<String, Object> model = modelView.getModel();
        
        // Sanitize sensitive data before rendering
        if (model.containsKey("password")) {
            model.remove("password");
        }
        
        // Add common data to all views
        model.put("appVersion", "2.1.0");
        model.put("currentTime", LocalDateTime.now());
    }
}

The afterCompletion Method

This method executes after the entire request processing chain completes, encluding view rendering. It's guaranteed to execute even if an exception occurred. Use it for:

  • Resource cleanup (closing database connections, file handles)
  • Performence logging
  • Audit trail recording
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                            Object handler, Exception ex) throws Exception {
    long startTime = (Long) request.getAttribute("startTime");
    long duration = System.currentTimeMillis() - startTime;
    
    if (duration > 1000) {
        log.warn("Slow request detected: {} took {}ms", request.getRequestURI(), duration);
    }
    
    if (ex != null) {
        log.error("Request failed with exception", ex);
    }
}

Multiple Interceptor Execution Flow

When multiple interceptors are configured, they form an interceptor chain with specific execution patterns:

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/orders/**"/>
        <bean class="com.example.mvc.interceptor.LoggingInterceptor"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/orders/**"/>
        <bean class="com.example.mvc.interceptor.ValidationInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

Execution sequence for a request to /orders/create:

LoggingInterceptor.preHandle          → First
ValidationInterceptor.preHandle       → Second
    ↓
    [Controller executes here]
    ↓
ValidationInterceptor.postHandle      → Second (reverse order)
LoggingInterceptor.postHandle          → First
    ↓
    [JSP/View renders]
    ↓
ValidationInterceptor.afterCompletion → Second (reverse order)
LoggingInterceptor.afterCompletion     → First

The execution follows a FILO (First In, Last Out) pattern:

  1. preHandle methods execute in configuration order
  2. postHandle methods execute in reverse configuration order
  3. afterCompletion methods execute in reverse configuration order

If any preHandle returns false, only that interceptor's afterCompletion is called for previously passed interceptors.

Practical Example: Authentication and Logging

public class AuditInterceptor implements HandlerInterceptor {
    
    private static final Logger auditLog = LoggerFactory.getLogger(AuditInterceptor.class);
    
    @Autowired
    private UserService userService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
                             Object handler) throws Exception {
        request.setAttribute("requestStartTime", System.currentTimeMillis());
        
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String userId = request.getRemoteUser();
        
        auditLog.info("Request initiated: {} {} by {}", method, uri, userId);
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                          Object handler, ModelAndView modelView) throws Exception {
        String viewName = modelView != null ? modelView.getViewName() : "no view";
        auditLog.debug("View resolved to: {}", viewName);
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) throws Exception {
        Long startTime = (Long) request.getAttribute("requestStartTime");
        long duration = System.currentTimeMillis() - startTime;
        
        auditLog.info("Request completed in {}ms with status: {}", 
                      duration, response.getStatus());
    }
}

This interceptor leverages Spring's dependency injection capability—something not possible with standard Servlet filters—demonstrating a key advantage of using interceptors for cross-cutting concerns within a Spring application.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.