In this article, I’ll guide you through an overview of how an HTTP request will be processed before it comes to the controller in Spring.
Before an HTTP request comes to the controller, it will come through Filter and Interceptor. Additionally, we can use AOP to customize what we can do before it comes to the controller(usually write log).
What is Filter?
A Filter is part of the Servlet API, not specific to Spring. It works at the lowest level, before Spring MVC kicks in.
🔧 Use cases:
-
Logging requests
-
Authentication / JWT token parsing
-
CORS handling
-
GZIP compression
-
Rate limiting
⚙️ How it works:
- It exists outside the Spring context, before the request thread reaches the DispatcherServlet, and acts on resources unrelated to Spring.
- Filters can be used to verify user request information, add or modify data, or change response information after resource processing.
- When the server is running and the servlet is coming up, Filter.init is called. Then doFilter is called.
- It wraps around the HttpServletRequest/Response.
- Filter.destroy is executed when the servlet terminates.
What is Interceptor ?
A Spring MVC feature that works with the handler (controller) level. It’s more Spring-specific and aware of the controller logic.
🔧 Use cases:
-
Logging user actions
-
Permission checks (based on roles)
-
Measuring execution time
-
Modifying ModelAndView
-
Custom headers before response
⚙️ How it works:
- Interceptor intercepts requests before and after processing.
- Interceptors process requests and responses to Controllers (Handlers) within the Spring context (scope) because they are inserted before and after Spring’s DispatcherServlet calls the Controller.
- It can access all of Spring’s bean objects. Multiple interceptors can be used and are used for tasks such as login check, authorization check, and program execution time calculation task log check.
What is AOP ?
AOP (Aspect-Oriented Programming) is a programming paradigm that lets you separate cross-cutting concerns from your core business logic.
In simpler terms: AOP lets you add behavior before, after, or around certain method executions — without modifying the actual code of those methods.
It is mainly used when adjusting more precisely in the business unit’s method, such as logging, transaction, and error handling.
Unlike Interceptor or Filter, it can be freely set before and after the method, and the target can be specified in various ways such as address, parameter, and annotation.
AOP’s @Advice is called using JoinPoint or ProceedingJoinPoint, not HttpServletRequest/Response.
Compare them
Servlet Filter, Spring Interceptor, and AOP are all functions used to perform actions before or after execution.
However, although they are similar in that they are about pre-processing of an action, AOP is used from a business perspective unlike filters and interceptors.
Mainly when implementing functions such as user sessions or authorization checks, you will be in a situation where you have to choose between Filter and Interceptor.
The two have similar roles in that they are pre-processing, but since the actual execution time and things that can be done are different, you can develop more effectively if you choose according to the requirements that need to be processed.
As you can see from above image, we usually authenticate JWT token in filter, because it does not go to the Spring application context.
Servlet Filter |
Interceptor |
AOP |
|
---|---|---|---|
Where to applied | Spring Context outside | Spring Context inside ~ Before Controller | Before and after the pointcut (method run) |
How to applied | Web Application(web.xml) | Spring Context(servlet-context.xml) | Source unit as Annotation, Pointcut, etc… |
Purpose | Global processing such as encoding, security, etc… | Detailed processing of requests, including authentication, authorization checks, etc… | Separate duplicate codes such as logging and error handling |
Example
- Create filter
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Slf4j
@Component
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("Before filter");
filterChain.doFilter(servletRequest, servletResponse); // pass to next filter or controller
log.info("After filter");
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
- Create Interceptor
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Component
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
log.info("Before Controller");
return true; // false = stop request
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("After Controller");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
log.info("After complete request");
}
}
- Add MyInterceptor to Spring Config
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/api/**"); // optional: limit to specific paths
}
}
- Create AOP to pointcut at controller
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class LoggingAspect {
@Before("execution(* com.ngoctuan.ecommerce.controller.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
log.info("Before: " + joinPoint.getSignature());
}
@After("execution(* com.ngoctuan.ecommerce.controller.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
log.info("After: " + joinPoint.getSignature());
}
}
- And let see the log when we call an API: