Applying the Chain of Responsibility Design Pattern in Spring MVC Interceptors
This article explains how the Chain of Responsibility design pattern is employed within Spring MVC interceptors, detailing the interceptor lifecycle, code implementation, differences from the classic GoF definition, and broader use cases such as authentication, logging, and order processing.
Introduction
The article discusses the application of the Chain of Responsibility design pattern in Spring MVC interceptors. It does not describe the internal workings of Spring MVC interceptors in depth, but references a related analysis of the HandlerInterceptor component.
What is the Chain of Responsibility?
The Chain of Responsibility pattern decouples the request sender from the receiver by arranging multiple handler objects in a linked structure. Each handler gets a chance to process the request; the request traverses the chain until a handler can handle it.
In practice, this means configuring a series of processor objects that are invoked sequentially for the same request.
Spring MVC Interceptor Workflow
When an HTTP request arrives, it first passes through the DispatcherServlet . The servlet’s doDispatch method uses the request URL and HandlerMapping to locate the appropriate controller.
Request enters the interceptor chain : The request is handed to the interceptor chain before reaching the controller.
Interceptor execution : Each interceptor can run custom logic at different stages via preHandle , postHandle , and afterCompletion methods. preHandle : Executed before controller processing; returning true continues the chain, false aborts it. postHandle : Executed after the controller method but before view rendering. afterCompletion : Executed after view rendering for cleanup.
Response returned to client : The DispatcherServlet sends the controller’s result (usually a view) back to the client.
Implementation Details in Spring MVC
The interceptor mechanism in Spring MVC is a concrete example of the Chain of Responsibility pattern. The framework stores configured interceptors in an interceptors array inside HandlerExecutionChain . During request dispatch, the chain’s applyPreHandle and applyPostHandle methods are invoked.
HandlerExecutionChain # applyPreHandle and applyPostHandle method information
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
// <1> Get interceptor array
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
// <2> Iterate over interceptors
for (int i = 0; i < interceptors.length; i++) {
// Determine if it can handle and then handle
}
}
// <4> Return true, pre‑handle succeeded
return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
// Reverse iterate over interceptors, omitted logic
}
}In Spring MVC, the DispatcherServlet calls applyPreHandle() before the controller logic and applyPostHandle() after it, effectively forming a responsibility chain.
Although Spring’s interceptor chain resembles a linked list, it is a variant of the classic pattern: the chain is stored as an array, and the framework iterates over it rather than using a true linked‑list structure.
Differences from the Classic GoF Definition
The GoF definition states that once a handler processes a request, the request stops propagating. Spring MVC interceptors deviate from this: even if a preHandle returns true , the request continues to subsequent interceptors and eventually reaches the controller. Only when preHandle returns false does the chain terminate early.
Other Typical Applications of the Chain of Responsibility
Beyond interceptors, the pattern is useful in many scenarios:
Authentication and Authorization : Multiple checks (e.g., identity verification, permission validation) can be chained, each passing the request to the next if it cannot handle it.
Order Processing : Steps such as validation, inventory check, payment, and shipping can be modeled as a chain, allowing early termination on failure.
Logging : Different log handlers (console, file, database) can be linked to process log messages based on severity or type.
Conclusion
While the GoF definition of the Chain of Responsibility emphasizes early termination, real‑world frameworks like Spring MVC often adapt the pattern so that all configured handlers are invoked unless a preHandle explicitly aborts the request. Understanding this adaptation helps developers design flexible interceptor chains for cross‑cutting concerns such as authentication, logging, and request modification.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.