Spring Boot Interceptors: Implementation and Source Code Analysis
This article delves into the implementation and underlying source code of Spring Boot interceptors, examining their role in request processing. We'll explore how to configure and utilize them, and trace their execution flow from request reception to response generation.
Configuring Interceptors
To implement an interceptor in Spring Boot, you typically create a configuration class that implements the WebMvcConfigurer interface. This interface provides a method, addInterceptors, where you can register your custom interceptor instances.
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyCustomInterceptor());
}
}
Custom Interceptor Example
A custom interceptor can extend HandlerInterceptorAdapter or implement the HandlerInterceptor interface directly. It provides methods like preHandle, postHandle, and afterCompletion that are invoked at different stages of the request processing lifecycle.
/**
* A sample custom interceptor.
*/
public class MyCustomInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Executing preHandle method...");
// Return true to continue processing, false to halt the request.
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Executing postHandle method...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Executing afterCompletion method...");
// This method is always called, even if an exception occurs.
// For demonstration, we'll simulate an exception here.
// if (ex == null) {
// System.out.println("Request completed successfully.");
// } else {
// System.out.println("Request completed with an exception: " + ex.getMessage());
// }
}
}
Interceptor Lifecycle and Spring Boot Bootstrapping
During Spring Boot startup, specifically within the doCreateBean phase, Spring Boot's auto-configuration (WebMvcAutoConfiguration) identifies and registers all classes implementing WebMvcConfigurer. Your custom configuration class, WebConfiguration, is among these, ensuring your interceptor is added to the framework's interceptor list.
Request Processing Flow
When a client request arrives, it follows a specific path:
- Tomcat Container: The request is first received by the embedded Tomcat server.
- DispatcherServlet: The request is then handed over to the
DispatcherServlet, the central handler in Spring MVC. - Filters: Before reaching Spring's interceptors, the request passes through Servlet filters. If a filter chain is not properly processed, the request might be halted here, preventing interceptor invocation. The creation of the filter chain involves mechanisms like
ApplicationFilterFactory.createFilterChain. - Interceptors: Once the request successfully passes through all configured filters, it proceeds to the Spring MVC interceptor chain.
Invoking Interceptors via DispatcherServlet
The DispatcherServlet's doDispatch method orchestrates the request handling, including interceptor calls. Here's a breakdown of key parts:
getHandler(processedRequest): Determines the appropriate handler (controller method) for the incoming request.applyPreHandle(processedRequest, response): This method iterates through all registered interceptors and calls theirpreHandlemethods. If anypreHandlemethod returnsfalse, request processing stops, and the interceptor chain is not further invoked. ThetriggerAfterCompletionmethod is called evenifpreHandlefails.ha.handle(...): If allpreHandlemethods returntrue, the actual handler method is invoked.applyPostHandle(...): After the handler method has executed (but before the view is rendered), thepostHandlemethods of the interceptors are called in reverse order.processDispatchResult(...): This method manages the outcome of the dispatch process.triggerAfterCompletion(...): Regardless of whether the request processing succeeded or threw an exception, theafterCompletionmethods of the interceptors are called. This is crucial for cleanup tasks. The source code fortriggerAfterCompletionshows it iterates through interceptors and calls theirafterCompletionmethod, handling potential exceptions gracefully to avoid disrupting the overall request flow.
The afterCompletion method is designed to be resilient; any exceptions thrown within it are caught and logged, but they do not prevent the final response from being sent back to the client.