Understanding Spring MVC Interceptors: Design, Configuration, and Custom Implementation
This article explains the design principles of Spring MVC interceptors, introduces the key interfaces and classes involved, demonstrates how they are wired within the framework, and provides step‑by‑step guidance for configuring and implementing custom interceptors in a Java web application.
Interceptors are an essential feature of every web framework and a frequently discussed topic.
This article analyzes how Spring MVC implements interceptor functionality, helping readers understand its design principles.
Important Interfaces and Classes
HandlerExecutionChain – composed of a HandlerMethod and a collection of Interceptors, obtained via HandlerMapping#getHandler.
HandlerInterceptor – the basic Spring MVC interceptor interface.
AbstractHandlerMapping – the abstract base class for HandlerMapping.
AsyncHandlerInterceptor – extends HandlerInterceptor to add afterConcurrentHandlingStarted for asynchronous requests.
HandlerInterceptorAdapter – an abstract class implementing AsyncHandlerInterceptor, commonly extended when creating custom interceptors.
WebRequestInterceptor – similar to HandlerInterceptor but without a return value for preHandle and operates on the request level.
MappedInterceptor – holds includePatterns and excludePatterns along with a HandlerInterceptor, used for selective URL interception.
ConversionServiceExposingInterceptor – added by to expose a ConversionService in each request.
Source Code Analysis
When a web request is captured by DispatcherServlet, its doDispatch method is invoked, leading to the creation of a HandlerExecutionChain. The chain’s applyPreHandle, applyPostHandle, and triggerAfterCompletion methods iterate over the configured HandlerInterceptor collection, invoking the corresponding lifecycle methods.
HandlerExecutionChain is obtained from HandlerMapping#getHandler. AbstractHandlerMapping builds the interceptor list by examining its internal interceptors collection, classifying each entry as a MappedInterceptor, HandlerInterceptor, or WebRequestInterceptor (the latter adapted via WebRequestHandlerInterceptorAdapter).
Interceptor Configuration
Two common ways to register interceptors:
Using in *-dispatcher.xml: <code><mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/login"/> <mvc:exclude-mapping path="/index"/> <bean class="package.interceptor.XXInterceptor"/> </mvc:interceptor> </mvc:interceptors></code>
Directly setting the interceptors property on RequestMappingHandlerMapping bean:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="interceptors">
<bean class="package.interceptor.XXInterceptor"/>
</property>
<property name="order" value="-1"/>
</bean>Both approaches ultimately produce MappedInterceptor instances.
Writing a Custom Interceptor
public class LoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Get request URI
String uri = request.getRequestURI();
// Allow login/auth and logout URLs
if (uri.endsWith("/login/auth") || uri.endsWith("/login/out")) {
return true;
}
// If accessing login page, redirect if already logged in
if (uri.endsWith("/login/") || uri.endsWith("/login")) {
if (request.getSession() != null && request.getSession().getAttribute("loginUser") != null) {
response.sendRedirect(request.getContextPath() + "/index");
} else {
return true;
}
}
// For other requests, allow if session contains loginUser
if (request.getSession() != null && request.getSession().getAttribute("loginUser") != null) {
return true;
}
// Otherwise redirect to login page
response.sendRedirect(request.getContextPath() + "/login");
return false;
}
}Login controller example:
@Controller
@RequestMapping("/login")
public class LoginController {
@RequestMapping({"/", ""})
public String index() {
return "login";
}
@RequestMapping("/auth")
public String auth(@RequestParam String username, HttpServletRequest req) {
req.getSession().setAttribute("loginUser", username);
return "redirect:/index";
}
@RequestMapping("/out")
public String out(HttpServletRequest req) {
req.getSession().removeAttribute("loginUser");
return "redirect:/login";
}
}Configuration can be simplified by adding entries for "/login/auth" and "/login/out" and removing the corresponding checks from the interceptor code.
Summary
The article summarizes the inner workings of Spring MVC interceptors, their configuration mechanisms, and why afterCompletion is still invoked even when preHandle returns false. It also touches on asynchronous request handling and alternative configuration methods for advanced use cases.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
