Spring Boot Filter、Interceptor、AOP 执行顺序深度拆解与生产实践

Spring Boot Filter、Interceptor、AOP 执行顺序深度拆解与生产实践

一、前言

在 Spring Boot 项目开发中,Filter(过滤器)、Interceptor(拦截器)、AOP(切面)是实现请求拦截、权限校验、日志统计、性能监控的核心组件。三者分属不同层级、执行顺序严格区分,也是日常开发 Bug 高发点、面试高频考点。

本文将通过源码层级拆解 + 可运行 Demo + 双场景实测 + 生产规范,彻底讲透三者的执行顺序、核心差异、使用场景及避坑方案。

二、实验环境版本

本次测试基于 Spring Boot3 最新稳定版本,适配 Servlet6.0 新特性,结论适配 Spring Boot3 全系列版本:

技术组件 对应版本
Spring Boot 3.2.7
JDK 21
Servlet API 6.0(内置 Tomcat 10.1)
Spring AOP 6.1.10

三、核心层级与组件定位(洋葱模型)

3.1 整体执行链路

三者遵循由外到内、层层包裹的洋葱模型,完整全链路执行顺序:

Servlet容器层(Filter) ➜ SpringMVC层(Interceptor前置) ➜ Bean代理层(AOP) ➜ Controller ➜ AOP后置 ➜ Interceptor后置 ➜ Filter后置

请求层级洋葱模型可视化:

Plain 复制代码
# 请求层级洋葱模型
┌────────────────────────────────────────────────────┐
│                  Servlet 容器                       │
│  ┌──────────────────────────────────────────────┐  │
│  │              Filter 层(Servlet原生)          │  │
│  │  ┌────────────────────────────────────────┐  │  │
│  │  │         DispatcherServlet 前端控制器    │  │  │
│  │  │  ┌──────────────────────────────────┐  │  │  │
│  │  │  │       Interceptor 层(SpringMVC)│  │  │  │
│  │  │  │  ┌────────────────────────────┐  │  │  │  │
│  │  │  │  │       AOP 层(Bean代理)   │  │  │  │  │
│  │  │  │  │  ┌──────────────────────┐  │  │  │  │  │
│  │  │  │  │  │    Controller Bean    │  │  │  │  │  │
│  │  │  │  │  └──────────────────────┘  │  │  │  │  │
│  │  │  │  └────────────────────────────┘  │  │  │  │
│  │  │  └──────────────────────────────────┘  │  │  │
│  │  └────────────────────────────────────────┘  │  │
│  └──────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────┘

3.2 三大组件核心特性详解

3.2.1 Filter(过滤器)- Servlet容器层

Filter 是Servlet 原生容器级组件,优先级最高,早于 SpringMVC 初始化,由 Tomcat 容器全权管理。

  • 层级:最外层,属于 Web 容器原生组件,脱离 Spring 框架依然生效

  • 容器归属 :不归 Spring 容器管理,即使加 @Component,也由 Servlet 容器实例化,无法直接 @Autowired 注入业务 Bean

  • 拦截粒度:粗粒度,仅基于 URL 路径匹配,无法感知 Controller 方法、注解

  • 执行特性chain.doFilter() 同步阻塞,后置代码一定会执行,不受 SpringMVC 拦截、业务异常影响

  • 适用场景:全局编码设置、CORS 跨域、请求流包装、GZIP 压缩、全局安全响应头配置

3.2.2 Interceptor(拦截器)- SpringMVC层

Interceptor 是SpringMVC 核心组件,由 DispatcherServlet 调度,本身是 Spring Bean,可直接使用 Spring 容器所有能力。

  • 层级:中间层,Spring 框架专属组件

  • 容器归属 :Spring 容器 Bean,支持 @Autowired 注入 Service、组件等业务类

  • 拦截粒度:细粒度,可精准感知目标 Controller 方法、支持路径匹配、排除、注解拦截

  • 生命周期:包含三大核心方法,执行规则严格固定

Interceptor 源码级执行规则

  1. preHandle 返回 false:直接终止 MVC 流程,postHandleafterCompletion 均不执行

  2. preHandle 返回 true:接口正常执行,三个方法全部执行

  3. preHandle 返回 true + 接口抛异常:postHandle 不执行,afterCompletion 依然执行

核心作用:业务层拦截,用于登录校验、Token 鉴权、接口限流、业务日志统计、参数校验

3.2.3 AOP(切面)- Spring Bean代理层

AOP 基于 Spring 动态代理机制,是最内层的 Bean 方法拦截组件,粒度最精细。

  • 层级:内层,仅作用于 Spring Bean 方法

  • 触发条件:必须成功进入 Controller 方法(走完 DispatcherServlet#doDispatch 完整流程)才会生效

  • 拦截限制:仅拦截 Spring Bean 方法调用,不感知 Servlet 请求;若被 Interceptor 前置拦截,AOP 完全不执行

  • 适用场景:方法级日志、接口耗时统计、事务控制、缓存切面、注解权限拦截、异常统一处理

3.3 极简记忆口诀

Filter 控制「请求能否进入应用」Interceptor 控制「请求能否进入接口方法」AOP 控制「接口方法如何执行」

四、Demo 项目结构

标准化分层结构,覆盖三大组件完整实现:

Plain 复制代码
spring-filter-interceptor/
├── pom.xml
└── src/main/java/com/coderplus/
    ├── Application.java                    # 项目启动类
    ├── config/WebConfig.java               # 拦截器注册配置(核心必写)
    ├── controller/OrderController.java     # 测试业务接口
    ├── filter/RequestLoggingFilter.java    # 全局请求耗时过滤器
    ├── interceptor/AuthInterceptor.java    # Token鉴权拦截器
    └── aspect/LoggingAspect.java           # 方法日志切面

五、全组件可运行代码实现

5.1 Filter 全局请求耗时过滤器

实现全局请求耗时统计、请求日志打印,适配所有接口:

Plain 复制代码
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
@Order(1)
public class RequestLoggingFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        String fullPath = req.getRequestURI()
                + (req.getQueryString() != null ? "?" + req.getQueryString() : "");

        System.out.println("[Filter] BEFORE: " + fullPath);
        long start = System.currentTimeMillis();

        // 同步阻塞:等待内层所有MVC、业务逻辑执行完毕
        chain.doFilter(request, response);

        // 无论内层是否拦截、是否异常,后置代码必定执行
        long elapsed = System.currentTimeMillis() - start;
        System.out.println("[Filter] AFTER : " + fullPath
                + " | 总耗时 " + elapsed + "ms");
    }
}

核心特性chain.doFilter() 为同步阻塞方法,SpringMVC 流程中断不会影响 Filter 后置执行。

5.2 Token 鉴权 Interceptor 拦截器

实现接口 Token 校验,无 Token 直接拦截返回 401:

Plain 复制代码
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) {
        String token = request.getHeader("Authorization");
        System.out.println("[Interceptor] preHandle: token=" + token);

        // 无Token直接拦截,返回401未授权
        if (token == null || token.isBlank()) {
            System.out.println("[Interceptor] preHandle → 返回 false,拦截请求");
            response.setStatus(401);
            return false;
        }

        System.out.println("[Interceptor] preHandle → 返回 true,放行");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) {
        System.out.println("[Interceptor] postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler, Exception ex) {
        String errMsg = ex != null ? " | 异常: " + ex.getMessage() : "";
        System.out.println("[Interceptor] afterCompletion" + errMsg);
    }
}

5.3 拦截器注册配置

关键知识点 :Filter 加 @Component 自动生效;Interceptor 仅加注解无法生效,必须手动注册到拦截器链

Plain 复制代码
import com.coderplus.interceptor.AuthInterceptor;
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 {

    private final AuthInterceptor authInterceptor;

    // 构造器注入Spring容器中的拦截器Bean
    public WebConfig(AuthInterceptor authInterceptor) {
        this.authInterceptor = authInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/api/**")           // 拦截所有业务接口
                .excludePathPatterns("/api/health");  // 放行健康检查接口
    }
}

5.4 Controller 层 AOP 日志切面

拦截所有 Controller 方法,实现方法执行日志、耗时统计:

Plain 复制代码
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    // 切点:拦截所有Controller层业务方法
    @Pointcut("execution(* com.coderplus.controller..*(..))")
    public void controllerLayer() {}

    @Around("controllerLayer()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("[AOP] @Around - 方法: "
                + joinPoint.getSignature().toShortString());
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        System.out.println("[AOP] @Around - 耗时: "
                + (System.currentTimeMillis() - start) + "ms");
        return result;
    }
}

5.5 测试业务 Controller

Plain 复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class OrderController {

    @GetMapping("/orders")
    public List<Map<String, Object>> getOrders(
            @RequestParam(defaultValue = "1") Long userId) {
        System.out.println("[Controller] OrderController.getOrders(userId="
                + userId + ")");
        return List.of(
                Map.of("orderId", "ORD20260709001", "amount", 99.90, "status", "已支付"),
                Map.of("orderId", "ORD20260708015", "amount", 256.00, "status", "已发货"),
                Map.of("orderId", "ORD20260701042", "amount", 18.50, "status", "已完成")
        );
    }
}

六、双场景实测:完整执行链路对比

项目启动命令:mvn spring-boot:run,分别测试正常放行请求拦截失败请求

6.1 正常请求(携带Token,完全放行)

测试命令:

Plain 复制代码
curl http://localhost:8080/api/orders?userId=1 -H "Authorization: Bearer xxx"

控制台完整执行顺序

  1. Filter BEFORE: /api/orders?userId=1

  2. Interceptor preHandle: token=Bearer xxx

  3. Interceptor preHandle → 返回 true,放行

  4. AOP @Around - 方法: OrderController.getOrders(Long)

  5. Controller 业务方法执行

  6. AOP @Around - 耗时: xxxms

  7. Interceptor postHandle

  8. Interceptor afterCompletion

  9. Filter AFTER : /api/orders?userId=1 | 总耗时 xxxms

标准完整链路:Filter前置 ➜ Interceptor前置(preHandle) ➜ AOP ➜ Controller ➜ AOP后置 ➜ Interceptor后置(postHandle/afterCompletion) ➜ Filter后置

6.2 非法请求(无Token,被拦截401)

测试命令:

Plain 复制代码
curl http://localhost:8080/api/orders?userId=1

控制台执行结果

  1. Filter BEFORE: /api/orders?userId=1

  2. Interceptor preHandle: token=null

  3. Interceptor preHandle → 返回 false,拦截请求

  4. Filter AFTER : /api/orders?userId=1 | 总耗时 1ms

6.3 核心现象总结(线上BUG根源)

  • AOP 完全不执行:请求未进入 Controller 方法,动态代理无法触发

  • Interceptor 后置方法失效:preHandle=false 直接截断 MVC 流程,postHandle、afterCompletion 均不执行

  • Filter 后置代码必执行:Servlet 容器生命周期独立,不受 SpringMVC 拦截、异常中断影响

七、Filter 与 Interceptor 生产选型对比

对比维度 Filter Interceptor
所属层级 Servlet 容器层(原生) SpringMVC 框架层
Spring Bean 注入 不归Spring容器管理,无法直接注入 Spring原生Bean,支持@Autowired注入
接口感知能力 仅识别URL,无法感知Controller方法 精准感知Handler、方法、注解
拦截粒度 粗粒度,仅URL匹配 细粒度,支持方法/注解精准拦截
核心适用场景 全局基础处理:编码、CORS、流包装、压缩、安全头 业务逻辑处理:权限、登录、限流、业务日志、指标统计

八、开发规范 & 面试核心总结

8.1 执行顺序铁律(必背)

Filter前置 ➜ Interceptor.preHandle ➜ AOP ➜ Controller ➜ AOP后置 ➜ Interceptor后置 ➜ Filter后置

8.2 高频踩坑点

  • Interceptor preHandle=false 仅终止 MVC 流程,不会终止 Filter 后置执行,极易造成监控数据失真

  • AOP 触发硬性条件:必须成功进入 Controller 方法,被 Interceptor 拦截的请求,AOP 完全失效

  • Interceptor 不能仅靠 @Component 生效,必须手动注册到 MVC 拦截器链

  • Filter 加 @Component 仅注册到 Servlet 容器,不属于 Spring Bean,无法直接注入业务类

8.3 生产强制开发规范

  • 接口有效请求统计、QPS、成功率、耗时打点,禁止写在 Filter 后置

  • 优先使用 Interceptor afterCompletion 做业务指标统计、日志收尾

  • afterCompletion源码规则:preHandle=true时,正常执行走 postHandle+afterCompletion,异常仅走 afterCompletion,是生产统计最优位置

相关推荐
程序员黑豆3 小时前
Java 注释详解:单行、多行与文档注释的完整指南
java·前端·ai编程
hey_sml4 小时前
JAVA每日一学---CompletableFuture中thenApply与thenCompose区别详解
java·开发语言
Doraemomo4 小时前
数据结构-环形链表
java·数据结构·链表
萧瑟余晖5 小时前
Java深入解析篇十七之SpringCloud
java·开发语言
是未才6 小时前
从输入 URL 到页面返回:DNS、路由、TLS 与 HTTP 完整链路
java·后端·计算机网络
ttod_qzstudio6 小时前
Java 常用语法极简通关(五):类与对象——字段、方法、构造器、this 与 static
java·开发语言·python
萧瑟余晖7 小时前
Java深入解析篇十七之Spring Security
java·开发语言·spring
不老刘8 小时前
【Java入门】Java服务部署方式全景对比:从java -jar到K8s
java·kubernetes·jar
离陌在学C#10 小时前
C# 重载与重写:深入理解面向对象编程的核心概念
java·c#
洛阳泰山10 小时前
AI 应用层被 Python 卷成红海,为什么我偏要用 Java 造一个 RAG + 工作流引擎?
java·人工智能·后端