Filters & Interceptors
IntermediateServlet Filters operate at the servlet container level for all requests; Spring HandlerInterceptors run around MVC dispatch and have access to handler metadata.
Overview
Spring Boot provides two mechanisms for intercepting HTTP requests before and after they reach a controller: Servlet Filters and Spring HandlerInterceptors. Filters are a Java Servlet API concept — they run in the servlet container layer, before Spring MVC processes the request. They see the raw HttpServletRequest and have no knowledge of which controller will handle the request. Interceptors are Spring MVC-specific — they run inside the DispatcherServlet after Spring has determined the handler method. This gives them access to handler metadata (controller class, method, model). Understanding which layer to use — and the difference in execution order — is a common interview and design question.
Servlet Filters — Container-Level Interception
Filters implement javax.servlet.Filter (Jakarta Servlet 5+ in Spring Boot 3). The doFilter() method wraps the full request/response lifecycle. Common uses: request logging, CORS headers, JWT token extraction, request/response body capture, compression.
Register filters in Spring Boot by annotating with @Component (registers for all paths), or using FilterRegistrationBean to control URL patterns and order.
// Custom filter — logs request/response timing
@Component
@Order(1) // lower number = higher priority
public class RequestLoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
long start = System.currentTimeMillis();
String requestId = UUID.randomUUID().toString().substring(0, 8);
req.setAttribute("requestId", requestId);
try {
chain.doFilter(request, response); // pass to next filter / servlet
} finally {
long duration = System.currentTimeMillis() - start;
log.info("[{}] {} {} → {} ({}ms)",
requestId, req.getMethod(), req.getRequestURI(),
resp.getStatus(), duration);
}
}
}
// Fine-grained registration — apply only to /api/**
@Bean
public FilterRegistrationBean<RequestLoggingFilter> loggingFilter() {
FilterRegistrationBean<RequestLoggingFilter> reg = new FilterRegistrationBean<>();
reg.setFilter(new RequestLoggingFilter());
reg.addUrlPatterns("/api/*");
reg.setOrder(1);
return reg;
}HandlerInterceptor — MVC-Level Interception
HandlerInterceptor runs after Spring MVC resolves the handler but before (preHandle) and after (postHandle / afterCompletion) execution. It has access to the HandlerMethod — you can inspect which controller/method will run, read method annotations, and conditionally reject or transform the request.
Common uses: role/permission checks based on method annotations, audit logging with controller name, locale/timezone injection per request.
// Interceptor — checks @RequiresRole annotation on controller methods
@Component
public class RoleCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res,
Object handler) throws Exception {
if (!(handler instanceof HandlerMethod hm)) return true; // non-handler request
RequiresRole annotation = hm.getMethodAnnotation(RequiresRole.class);
if (annotation == null) return true; // no role restriction
String userRole = (String) req.getAttribute("userRole"); // set by JWT filter
if (!annotation.value().equals(userRole)) {
res.sendError(HttpServletResponse.SC_FORBIDDEN, "Insufficient role");
return false; // abort request processing
}
return true; // continue
}
@Override
public void postHandle(HttpServletRequest req, HttpServletResponse res,
Object handler, ModelAndView modelAndView) {
// called after handler but before view rendering (rarely needed in REST APIs)
}
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse res,
Object handler, Exception ex) {
// called after full request lifecycle — good for cleanup / metrics
if (ex != null) log.error("Request failed with exception", ex);
}
}
// Register with Spring MVC
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired RoleCheckInterceptor roleCheck;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(roleCheck).addPathPatterns("/api/**");
}
}Filter vs Interceptor — Choosing the Right Layer
The key differences:
| Aspect | Filter | Interceptor | |--------|--------|-------------| | Layer | Servlet container | Spring MVC | | Handler access | No | Yes (HandlerMethod) | | Works without Spring MVC | Yes | No | | Exception handling | Manual | Handled by @ControllerAdvice | | Request body reading | Can read stream once | Request body already read |
**Use a Filter for**: CORS, security (JWT extraction), request/response logging, content encoding, anything that must apply to ALL requests including static resources.
**Use an Interceptor for**: business-logic cross-cutting concerns that need handler metadata (annotation-based permissions, auditing with controller name, per-endpoint rate limiting).
// Execution order for a request:
// 1. Filter 1 preHandle
// 2. Filter 2 preHandle
// 3. Spring DispatcherServlet
// 4. Interceptor 1 preHandle
// 5. Interceptor 2 preHandle
// 6. Controller method executes
// 7. Interceptor 2 postHandle
// 8. Interceptor 1 postHandle
// 9. View rendered (or @ResponseBody serialised)
// 10. Interceptor 2 afterCompletion
// 11. Interceptor 1 afterCompletion
// 12. Filter 2 postHandle (chain.doFilter returns)
// 13. Filter 1 postHandle
// Spring Security uses Filters (not Interceptors) because it needs to run
// before Spring MVC determines the handler — authentication cannot wait.Key Points to Remember
- 1Filters run at the servlet container level — before Spring MVC. Interceptors run inside DispatcherServlet — after handler resolution.
- 2Interceptors have access to HandlerMethod — you can read method annotations and controller class for context-aware logic.
- 3Spring Security uses Filters (not Interceptors) because security must run before MVC handler resolution.
- 4Register filters with @Component (all paths) or FilterRegistrationBean (specific patterns + order control).
- 5Register interceptors with WebMvcConfigurer.addInterceptors() and scope them with addPathPatterns().
- 6Filters cannot access Spring beans directly unless using DelegatingFilterProxy; Interceptors are Spring-managed and can @Autowire anything.
Interview Questions
Sign in to ask AriaWhat is the difference between a Servlet Filter and a Spring HandlerInterceptor?
Why does Spring Security use Filters rather than Interceptors?
How do you restrict a filter to only apply to specific URL patterns?
How do you read a method-level annotation from inside a HandlerInterceptor?
What is the execution order of filters and interceptors in a Spring Boot request lifecycle?
Ask Aria about Filters & Interceptors
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.