Interceptors in Spring Boot and Spring MVC

Interceptors in Spring Boot and Spring MVC

In Java web development, an Interceptor is a powerful tool used to execute custom logic before a request reaches the controller or after a response is returned to the client. Compared to traditional @PreHandle, @PostHandle, and @PostConvert annotations, the interceptor mechanism provided by Spring MVC offers greater flexibility and generality, making it particularly suitable for scenarios requiring unified handling of security authentication, logging, or performance monitoring across multiple Controllers. This article delves into the configuration principles, core interface definitions, and practical application scenarios of interceptors within a Spring Boot environment.
The interceptor mechanism in Spring MVC is essentially a dynamic proxy technology based on AOP (Aspect-Oriented Programming). When an interceptor is enabled, the Spring container inserts specific code blocks during request processing. These blocks can be invoked before the Controller method executes for pre-processing or after the Controller method execution but before the response is sent for post-processing.

Unlike Filters, which run within the Servlet lifecycle and have limited access to internal objects, Interceptors operate after the Servlet lifecycle has begun. They can directly access Spring MVC's Handler and ResponseBodyWriter objects among others, thereby gaining more detailed request context information. This characteristic makes interceptors more convenient than filters when handling complex business logic.

Core Interfaces and Implementation Steps

To create a custom interceptor, you first need to inherit the interface provided by Spring: HandlerInterceptor. This interface defines three core methods: preHandle, postHandle, and afterCompletion.

  1. preHandle: Invoked before request processing begins. If this method returns false, the request will be terminated, and subsequent Controller logic will not execute.
  2. postHandle: Invoked after the Controller method has completed but before the response is generated. At this point, you can modify the ResponseBodyWriter to intercept content intended for the client.
  3. afterCompletion: Regardless of whether the request was processed successfully or an exception occurred, this method will always be invoked. It is commonly used for resource cleanup or global logging.

Here is a basic implementation example:

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // User authentication logic can be implemented here
        System.out.println("Request entering interceptor: " + request.getRequestURI());
        return true; // Allow execution to continue
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // View name modification or response header interception can be implemented here
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("Request processing completed: " + (ex != null ? "Exception occurred" : "Success"));
    }
}

Registering Interceptors in Spring Boot

In a Spring Boot application, there are two primary ways to register interceptors: automatic scanning and registration via the @Component annotation, or manually adding them to an interceptor list within WebMvcConfigurer.

1. Using @Component Annotation

This is the simplest approach. During startup, Spring Boot scans for components in the classpath; any class implementing the HandlerInterceptor interface with a @Component annotation will be automatically registered as an interceptor.

@Component
public class AuthInterceptor implements HandlerInterceptor {
    // Implementation logic...
}

2. Using WebMvcConfigurer Configuration

For more fine-grained control, such as adding interceptors based on priority, the WebMvcConfigurer interface can be used. In Spring Boot, configuration is typically completed by inheriting WebMvcConfigurerAdapter (Spring Boot 2.x) or implementing WebMvcConfigurer directly (Spring Boot 3.x).

public class MyWebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // Define interceptor rules: match all requests, excluding /static/** and /js/**
        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/static/**", "/js/**");
    }
}

Practical Application Scenarios Examples

1. Unified Permission Control

Interceptors are frequently used to manage user login states. In the preHandle method, you can check for the existence of a Session or Token. If one does not exist, return an HTTP 401 Unauthorized status code and redirect to the login page; if it exists, allow the request through. This approach avoids duplicating identical judgment logic in every Controller, significantly improving code maintainability.

2. Global Logging

Interceptors can be used to uniformly log API request parameters, response times, and execution results. Record the start time of the request in preHandle, calculate duration, and write logs to a file in afterCompletion. This is crucial for monitoring and troubleshooting in production environments.

public class LogInterceptor implements HandlerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long startTime = System.currentTimeMillis();
        String uri = request.getRequestURI();
        logger.info("Request processing started: {}", uri);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        long endTime = System.currentTimeMillis();
        String uri = request.getRequestURI();
        int status = response.getStatus();
        logger.info("Request {} completed. Duration: {}ms. Status code: {}", uri, (endTime - startTime), status);
    }
}

3. Data Desensitization Processing

For interfaces involving sensitive information, uniform desensitization of parameters or responses can be performed within the interceptor. For example, before returning JSON data, fields such as ID numbers and phone numbers can be replaced with asterisks to ensure user privacy security.

Precautions and Best Practices

When using interceptors, there are several key points to note:

  • Execution Order: The execution order of interceptors depends on the order in which they were added to InterceptorRegistry or follows Bean definition order (in Spring Boot 2.x). It is essential to clearly define the sequence of business logic execution.
  • Exception Handling: If an exception is thrown within the preHandle method, the request will terminate immediately without entering Controller logic. Therefore, exceptions must be handled carefully.
  • Performance Impact: While interceptors offer convenience, they should not contain time-consuming database queries or complex calculations to avoid blocking all requests.

By reasonably designing and utilizing the interceptor mechanism of Spring Boot and Spring MVC, developers can build more robust, secure, and maintainable web applications. Mastering this technology is an important step in improving Java web development efficiency.