Backend Development 14 min read

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 Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring Boot 3 Interception: Filters, Security, Interceptors & AOP

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

@ServletComponentScan

to activate servlet components.

2.2 Spring Security Filter

Overview : Spring Security builds a

SecurityFilterChain

that registers a

FilterChainProxy

containing 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

DispatcherServlet

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

BackendAOPSpring BootFiltersInterceptorsspring security
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.