Home/Learn/Spring Boot/Filters & Interceptors

Filters & Interceptors

Intermediate
Web / REST

Servlet 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.

Java — Servlet Filter
// 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.

Java — HandlerInterceptor
// 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).

Java (comments) — Execution Order
// 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 Aria
1

What is the difference between a Servlet Filter and a Spring HandlerInterceptor?

MediumAmazon
2

Why does Spring Security use Filters rather than Interceptors?

MediumNetflix
3

How do you restrict a filter to only apply to specific URL patterns?

EasyFlipkart
4

How do you read a method-level annotation from inside a HandlerInterceptor?

MediumUber
5

What is the execution order of filters and interceptors in a Spring Boot request lifecycle?

HardGoogle

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.

Loading discussion…