Master Spring Boot 3 Interception: Filters, Security, Interceptors & AOP
This article explains the various interception mechanisms in Spring Boot 3—including Filters, Spring Security filters, MVC Interceptors, AOP, ControllerAdvice, Request/ResponseBodyAdvice, and REST Clients—provides execution flow diagrams, detailed code examples, and practical usage tips for building robust backend applications.
Spring Boot 3 offers multiple interception mechanisms that let developers insert custom logic at different stages of request processing, enabling unified handling, security control, logging, and more.
Filter : Based on the Servlet specification, intercepts all HTTP requests for preprocessing such as encoding and CORS handling; runs earliest in the chain.
Spring Security : Uses a FilterChainProxy to provide authentication (OAuth2/JWT) and authorization (RBAC) capabilities.
Interceptor : Operates at the Spring MVC layer, intercepting controller requests for tasks like permission checks and logging.
AOP : Intercepts method calls to add cross‑cutting concerns such as transaction management or performance monitoring.
ControllerAdvice : Globally handles controller exceptions, data binding, and parameter preprocessing.
RequestBodyAdvice/ResponseBodyAdvice : Intervenes in request/response body serialization for tasks like encryption or unified response wrapping.
REST Clients : Intercepts outgoing REST calls, allowing request modification and response processing.
1. Introduction
These mechanisms cover the full flow from low‑level data streams to business logic, and developers can combine them flexibly according to their needs.
2. Practical Examples
2.1 Filter
Overview : Filters intercept all HTTP requests based on the Servlet spec, handling preprocessing and post‑processing.
Execution Position :
<code>[Browser] -> [Filter 1] -> [Filter 2] -> ... -> [Servlet Container]</code>Example :
<code>@WebFilter("/*")
public class DemoFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.err.println("filter before...");
chain.doFilter(request, response);
System.err.println("filter after...");
}
}
</code>Note: Enable
@ServletComponentScanto activate servlet components.
2.2 Spring Security Filter
Overview : Spring Security builds a
SecurityFilterChainthat registers a
FilterChainProxycontaining many internal filters, providing authentication, authorization, CSRF protection, etc.
Differences from native Filter :
Registration: Native filters are directly registered with the servlet container; Spring Security filters are registered indirectly via
FilterChainProxy.
Management: Spring Security manages its filters internally.
Flexibility: Allows dynamic configuration based on request path, method, or other conditions.
Execution Position :
<code>[Browser] -> [Other Filters] -> [Spring Security Filter] -> [Servlet Container]</code>Example (adding a custom filter to the security chain):
<code>@Bean
SecurityFilterChain apiSecurity(HttpSecurity http) throws Throwable {
http.csrf(csrf -> csrf.disable());
http.securityMatcher("/api/**", "/login");
http.formLogin(Customizer.withDefaults());
http.authorizeHttpRequests(registry -> {
registry.anyRequest().authenticated();
});
// Add custom filter before UsernamePasswordAuthenticationFilter
http.addFilterBefore(new TokenFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
public class TokenFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
System.err.println("token valid...");
filterChain.doFilter(request, response);
}
}
</code>2.3 Interceptor
Overview : Interceptors work at the Spring MVC level, sitting between
DispatcherServletand the controller, suitable for permission checks and logging.
Execution Position :
<code>[Browser] -> [Filter Chain] -> [DispatcherServlet] -> [Interceptor] -> [Controller]</code>Example :
<code>// 1. Define interceptor
public class DemoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.err.println("pre‑handle...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.err.println("post‑handle after controller...");
}
}
// 2. Register interceptor
@Component
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/business/*");
}
}
// 3. Controller
@RestController
@RequestMapping("/business")
public class BusinessController {
@GetMapping("/index")
public ResponseEntity<?> index() {
System.err.println("controller execution...");
return ResponseEntity.ok("business index...");
}
}
</code>2.4 AOP
Overview : AOP intercepts method calls to implement cross‑cutting concerns such as transaction management or performance monitoring.
Execution Position :
<code>... -> [AOP Aspect] -> [Target Method]</code>Example :
<code>// 1. Define aspect
@Aspect
@Component
public class LogAspect {
private static final Logger logger = LoggerFactory.getLogger("LogAspect");
@Around("execution(* com.pack.resp.service.BusinessService.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
logger.info("before log...");
Object ret = pjp.proceed();
logger.info("after log...");
return ret;
}
}
// 2. Business service
@Service
public class BusinessService {
private static final Logger logger = LoggerFactory.getLogger("UserService");
public void save() {
logger.info("BusinessService save...");
}
}
// 3. Controller endpoint
@GetMapping("/save")
public ResponseEntity<?> save() {
logger.info("controller #save...");
businessService.save();
return ResponseEntity.ok("business save...");
}
</code>2.5 ControllerAdvice
Overview : Provides global exception handling, data binding, and parameter preprocessing for controllers.
Execution Position :
<code>[ControllerAdvice @InitBinder/@ModelAttribute] -> [Controller] -> [ControllerAdvice (exception handling)]</code>Example :
<code>@RestControllerAdvice
public class GlobalControllerAdvice {
private static final Logger logger = LoggerFactory.getLogger("ControllerAdvice");
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handle(Exception e) {
e.printStackTrace();
logger.info("@ExceptionHandler...");
return ResponseEntity.ok(e.getMessage());
}
@ModelAttribute("info")
public Map<String, Object> info() {
logger.info("@ModelAttribute...");
return Map.of("name", "pack");
}
@InitBinder
public void binder(WebDataBinder binder) {
logger.info("@InitBinder...");
}
}
@RestController
public class SampleController {
@GetMapping("/save")
public ResponseEntity<?> save(User user) {
logger.info("controller #save...");
businessService.save();
return ResponseEntity.ok("business save...");
}
}
</code>2.6 RequestBodyAdvice / ResponseBodyAdvice
Overview : Intercept request and response bodies for tasks such as encryption, decryption, or unified response formatting.
Execution Position :
<code>[RequestBodyAdvice] -> [Controller] -> [ResponseBodyAdvice]</code>Example :
<code>// Request processor
@ControllerAdvice
public class RequestProcessor extends RequestBodyAdviceAdapter {
private static final Logger logger = LoggerFactory.getLogger("RequestProcessor");
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
logger.info("RequestBodyAdvice before...");
return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);
}
}
// Response processor
@ControllerAdvice
public class ResponseProcessor implements ResponseBodyAdvice<Object> {
private static final Logger logger = LoggerFactory.getLogger("ResponseProcessor");
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
logger.info("ResponseBodyAdvice before...");
return body;
}
}
// Controller endpoint
@PostMapping("/create")
public ResponseEntity<?> create(@RequestBody User user) {
logger.info("controller #create...");
return ResponseEntity.ok("business create...");
}
</code>2.7 REST Clients
Overview : Used to call external REST APIs; interceptors can process requests and responses.
Execution Position :
<code>[Application] -> [REST Client] -> [External REST API]</code>Example :
<code>// Custom interceptor
public class LoggerRequestInterceptor implements ClientHttpRequestInterceptor {
private static final Logger logger = LoggerFactory.getLogger("ClientHttpRequestInterceptor");
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logger.info("Rest Client request before...");
ClientHttpResponse response = execution.execute(request, body);
logger.info("Rest Client response after...");
return response;
}
}
// RestTemplate bean
@Bean
RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.interceptors(new LoggerRequestInterceptor()).build();
}
// RestClient bean (Spring 6+)
@Bean
RestClient restClient(RestClient.Builder builder) {
return builder.requestInterceptor(new LoggerRequestInterceptor()).build();
}
// Example call
@GetMapping("/invoke")
public ResponseEntity<?> invoke() {
String url = "http://localhost:8080/business/index";
String data = this.restClient.get().uri(url).retrieve().body(String.class);
return ResponseEntity.ok(data);
}
</code>These examples demonstrate how to leverage each interception mechanism in a Spring Boot 3 application to build flexible, maintainable, and secure backend services.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
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.