Spring Boot Interceptor: Understanding with Real-Time Example

Introduction

A spring boot interceptorΒ is a component that allows you to intercept incoming HTTP requestsΒ before they reach the controllerΒ and optionally intercept the responseΒ after the controller is done.

It’s similar to a servlet filter but works at the Spring MVC layer.

You can use interceptors to:

  • Add common logic like authentication, logging, or trace ID injection
  • Modify request or response headers
  • Block a request conditionally

πŸ§ͺ Basic Example of an Sprint Boot Interceptor

Let’s say we want to log every incoming request URL.

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class SimpleInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        log.info("[Request URI] {}", request.getRequestURI());
        return true; // continue processing
    }
}

You then register it like this:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private SimpleInterceptor simpleInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(simpleInterceptor);
    }
}

πŸ’‘ Now Let’s Understand with a Real-Time Use Case

Imagine you are building an API where you need to:

  • Add a trace ID to every request
  • Log specific headers like User-Agent and Authorization
  • Allow configuration of which headers to log from application.yml

This is how we can do it:

🧱 Create the Spring Boot Interceptor

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class LoggingInterceptor implements HandlerInterceptor {

    @Value("${log.headers:*}")
    private String logHeaders;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        String traceId = UUID.randomUUID().toString();
        request.setAttribute("traceId", traceId);
        response.setHeader("X-Trace-Id", traceId);

        // Optional: Add traceId to MDC for logging frameworks
        MDC.put("traceId", traceId);

        log.info("[TRACE-ID] {}", traceId);

        List<String> headersToLog = Arrays.asList(logHeaders.split(","));

        log.info("[HEADERS]");
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            if (logHeaders.equals("*") || headersToLog.contains(name)) {
                log.info("{}: {}", name, request.getHeader(name));
            }
        }

        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // Clean up MDC to avoid leaking traceId in async/threaded environments
        MDC.remove("traceId");
    }
}

πŸ”§ Register the Spring Boot Interceptor with Include/Exclude Patterns and Ordering

Let’s say you only want to intercept /api/** requests and skip some specific endpoints like /api/health.

You may also want to register multiple interceptors and control their execution order.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoggingInterceptor loggingInterceptor;

    @Autowired
    private SimpleInterceptor simpleInterceptor;

    @Value("${interceptor.include-paths:/api/**}")
    private String[] includePaths;

    @Value("${interceptor.exclude-paths:/api/health}")
    private String[] excludePaths;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // Order matters: interceptors are called in registration order
        registry.addInterceptor(simpleInterceptor)
                .addPathPatterns("/**")
                .order(1);

        registry.addInterceptor(loggingInterceptor)
                .addPathPatterns(includePaths)
                .excludePathPatterns(excludePaths)
                .order(2);
    }
}
  • Interceptors with lower order() values run first.
  • You can omit .order() to use default order (0).

βš™οΈ application.yml

log:
  headers: "User-Agent,Authorization"

interceptor:
  include-paths:
    - "/api/**"
  exclude-paths:
    - "/api/health"

This makes your interceptor setup flexible and easily configurable.


🧾 Example Request and Output

Request headers:

User-Agent: PostmanRuntime/7.32.3
Authorization: Bearer xyz
Content-Type: application/json

Console Output:

[TRACE-ID] 8a7f1c2e-bd2d-4f4d-a61f-890x...
[HEADERS]
User-Agent: PostmanRuntime/7.32.3
Authorization: Bearer xyz

πŸ“¦ Sending Logs to Splunk, Kibana, or Dynatrace (Optional)

Once the traceId is added to the MDC (Mapped Diagnostic Context), your logger (like Logback or Log4j2) can include it in every log line.

This helps if you are pushing logs to external systems like Splunk, Kibana, or Dynatrace.

Example log pattern (Logback):

<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - traceId=%X{traceId} %msg%n</pattern>

This way:

  • Your logs include traceId=… for each request
  • If logs are pushed to SplunkDynatrace, or ELK, you can filter and trace by traceId

πŸ“ Note: This is optional. You only need to configure MDC and external log output if you want to send logs to tools like Splunk or Kibana. For local console logging or debugging, it is not required.

βœ… Most APM tools like Dynatrace can also auto-capture custom headers like X-Trace-Id, so you can correlate logs and traces easily.


βœ… Summary

  • Interceptors are useful for request/response pre-processing in Spring MVC
  • You can register them easily via WebMvcConfigurer
  • They are perfect for use cases like logging, trace ID injection, and authentication
  • Using application.yml makes logging headers and URL paths configurable and dynamic
  • You can register multiple interceptors and control their execution order using .order()
  • Optional: Use MDC + structured logging to trace across systems like Splunk, ELK, or Dynatrace